mirror of
https://github.com/wasrusgen/wasrusgen1-crm.git
synced 2026-06-03 14:24:47 +00:00
add: Mokap mockups, CLAUDE.md, project structure
- 15 HTML mockups (admin, assembler, manager, owner и др.) - CLAUDE.md с политикой работы с файлами - .claude/launch.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2095c56307
commit
ffa56b1d18
11
.claude/launch.json
Normal file
11
.claude/launch.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.0.1",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "crm-docs",
|
||||
"runtimeExecutable": "python",
|
||||
"runtimeArgs": ["-m", "http.server", "7842", "--directory", "D:/! Рабочий стол/!!! GOOGLE DISK/AI CRM/docs"],
|
||||
"port": 7842
|
||||
}
|
||||
]
|
||||
}
|
||||
76
CLAUDE.md
Normal file
76
CLAUDE.md
Normal file
@ -0,0 +1,76 @@
|
||||
# CRM — @wasrusgen1
|
||||
> Автоматизация процессов · Бренд: индиго #4338CA
|
||||
|
||||
## Статус
|
||||
В разработке. Мокапы и бэкенд созданы.
|
||||
|
||||
## Архитектура
|
||||
```
|
||||
backend\ — серверная часть
|
||||
Mokap\ — рабочие HTML-прототипы (редактировать здесь)
|
||||
docs\ — публикуемая копия (GitHub Pages)
|
||||
ПРЕЗЕНТАЦИИ\ — клиентские презентации
|
||||
АУДИО\ — TTS / голосовые материалы
|
||||
```
|
||||
- **Репо**: github.com/wasrusgen/wasrusgen1-crm
|
||||
- **Pages**: https://wasrusgen.github.io/wasrusgen1-crm/
|
||||
- **Деплой мокапов**: cp Mokap → docs → git push
|
||||
|
||||
## Правила работы с мокапами
|
||||
1. Редактировать в `Mokap\`
|
||||
2. Копировать в `docs\` перед пушем
|
||||
3. Скриншот через Chrome MCP после каждого изменения
|
||||
4. Ссылка на Pages в конце каждого сообщения
|
||||
|
||||
## Метрики (отслеживать)
|
||||
- Клиенты: 0 → цель 3 в первый квартал
|
||||
- MRR (ежемесячная выручка)
|
||||
- Активные пользователи / мес
|
||||
- Churn rate
|
||||
|
||||
## Текущие задачи
|
||||
- [ ] Завершить UI мокапов
|
||||
- [ ] Подключить бэкенд к мокапам
|
||||
- [ ] Настройка онбординга
|
||||
- [ ] Ценообразование и лендинг
|
||||
|
||||
## Модели и сценарии
|
||||
- Генерация HTML/CSS мокапов → Sonnet
|
||||
- Бэкенд, API, скрипты → Haiku/Sonnet
|
||||
- Архитектура новых модулей → Sonnet
|
||||
- Продуктовая стратегия → уточнить у Руслана
|
||||
|
||||
## Связи
|
||||
- Брендбук: D:\...\БРЕНДЫ\brandbook_crm.html
|
||||
- GitHub: wasrusgen/wasrusgen1-crm
|
||||
- Токены: Bitwarden vault
|
||||
|
||||
## Политика работы с файлами
|
||||
|
||||
### Расположение файлов
|
||||
| Среда | Путь |
|
||||
|---|---|
|
||||
| Локально (Windows) | `D:\! Рабочий стол\!!! GOOGLE DISK\AI CRM\` |
|
||||
| Timeweb VPS | `/opt/projects/crm/` |
|
||||
| Клиентские файлы | Яндекс S3: `wasrusgen1-crm` |
|
||||
|
||||
### Синхронизация
|
||||
- **Автоматически** каждый час: локальный ПК → Timeweb VPS
|
||||
- Скрипт: `C:\Users\WASRUSGEN\.claude\sync\sync-projects.ps1`
|
||||
- Исключения: `.git/`, `__pycache__/`, `node_modules/`, `_backups/`
|
||||
|
||||
### Доступ с любого компьютера
|
||||
```bash
|
||||
# Получить все файлы проекта на новой машине:
|
||||
rclone sync timeweb:/opt/projects/crm/ ./ --sftp-host 94.241.170.144
|
||||
```
|
||||
|
||||
### Приоритет источников для Claude
|
||||
1. Локальные файлы (если открыта сессия на основном ПК)
|
||||
2. Timeweb VPS /opt/projects/ (если другое устройство)
|
||||
3. GitHub repo wasrusgen/claude-config (CLAUDE.md и настройки)
|
||||
|
||||
### Ключи и секреты
|
||||
- Хранятся в **Bitwarden**: `vault-94-241-170-144.sslip.io`
|
||||
- Локально: `C:\Users\WASRUSGEN\.claude\secrets.md`
|
||||
- **Не коммитить** в git, не копировать в облако
|
||||
733
Mokap/consult_demo.html
Normal file
733
Mokap/consult_demo.html
Normal file
@ -0,0 +1,733 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI КОНСАЛТИНГ CRM — Демо</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg: #0F172A;
|
||||
--bg2: #1E293B;
|
||||
--bg3: #152033;
|
||||
--sidebar: #0D1829;
|
||||
--accent: #047857;
|
||||
--accent2: #10B981;
|
||||
--accent3: #34D399;
|
||||
--text: #F1F5F9;
|
||||
--muted: #94A3B8;
|
||||
--border: #1E3A5F;
|
||||
--border2: #334155;
|
||||
--danger: #EF4444;
|
||||
--warning: #F59E0B;
|
||||
--info: #3B82F6;
|
||||
}
|
||||
html, body { height: 100%; }
|
||||
body { background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; font-size: 14px; -webkit-font-smoothing: antialiased; display: flex; flex-direction: column; min-height: 100vh; }
|
||||
|
||||
/* TOP NAV */
|
||||
.top-nav {
|
||||
background: var(--sidebar); border-bottom: 1px solid var(--border);
|
||||
padding: 0 24px; height: 56px; display: flex; align-items: center; gap: 0;
|
||||
flex-shrink: 0; position: sticky; top: 0; z-index: 50;
|
||||
}
|
||||
.nav-logo { font-family: 'Montserrat', sans-serif; font-weight: 800; font-size: 17px; color: var(--accent2); margin-right: 32px; white-space: nowrap; }
|
||||
.nav-logo span { color: var(--muted); }
|
||||
.tab-bar { display: flex; gap: 2px; flex: 1; overflow-x: auto; }
|
||||
.tab {
|
||||
padding: 8px 16px; border-radius: 7px; font-size: 13px; font-weight: 500;
|
||||
color: var(--muted); cursor: pointer; white-space: nowrap; transition: all 0.15s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.tab:hover { background: rgba(255,255,255,0.05); color: var(--text); }
|
||||
.tab.active { background: rgba(4,120,87,0.15); color: var(--accent2); border-color: rgba(4,120,87,0.3); }
|
||||
.nav-badge { margin-left: 16px; background: rgba(4,120,87,0.15); color: var(--accent2); padding: 4px 12px; border-radius: 100px; font-size: 12px; font-weight: 600; border: 1px solid rgba(4,120,87,0.3); white-space: nowrap; }
|
||||
|
||||
/* SCREENS */
|
||||
.screen { display: none; flex: 1; overflow-y: auto; }
|
||||
.screen.active { display: flex; flex-direction: column; }
|
||||
|
||||
/* ─── SCREEN 1: WORKSPACE ─── */
|
||||
.workspace { display: flex; height: calc(100vh - 56px); }
|
||||
.ws-sidebar {
|
||||
width: 260px; flex-shrink: 0; background: var(--sidebar); border-right: 1px solid var(--border);
|
||||
display: flex; flex-direction: column; padding: 20px 16px;
|
||||
}
|
||||
.ws-sidebar h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); font-weight: 600; margin-bottom: 12px; padding: 0 4px; }
|
||||
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; margin-bottom: 24px; }
|
||||
.sidebar-item {
|
||||
padding: 8px 12px; border-radius: 8px; font-size: 13px; color: var(--muted);
|
||||
cursor: pointer; display: flex; align-items: center; gap: 8px; transition: all 0.15s;
|
||||
}
|
||||
.sidebar-item:hover { background: rgba(255,255,255,0.05); color: var(--text); }
|
||||
.sidebar-item.active { background: rgba(4,120,87,0.12); color: var(--accent2); }
|
||||
.sidebar-item .icon { font-size: 15px; width: 20px; text-align: center; }
|
||||
.ws-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
|
||||
.ws-header {
|
||||
padding: 16px 24px; border-bottom: 1px solid var(--border);
|
||||
background: var(--bg2); display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.ws-header-left h2 { font-size: 16px; font-weight: 700; }
|
||||
.ws-header-left p { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
||||
.status-badge { padding: 4px 10px; border-radius: 100px; font-size: 11px; font-weight: 600; }
|
||||
.status-active { background: rgba(52,211,153,0.12); color: var(--accent3); border: 1px solid rgba(52,211,153,0.25); }
|
||||
|
||||
.banners { padding: 16px 24px 0; display: flex; flex-direction: column; gap: 10px; }
|
||||
.banner {
|
||||
border-radius: 10px; padding: 14px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
||||
}
|
||||
.banner-info { display: flex; align-items: center; gap: 10px; }
|
||||
.banner-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
||||
.banner.blue { background: rgba(59,130,246,0.1); border: 1px solid rgba(59,130,246,0.25); }
|
||||
.banner.blue .banner-dot { background: var(--info); }
|
||||
.banner.green { background: rgba(4,120,87,0.1); border: 1px solid rgba(4,120,87,0.3); }
|
||||
.banner.green .banner-dot { background: var(--accent2); }
|
||||
.banner-title { font-size: 13px; font-weight: 600; }
|
||||
.banner-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
|
||||
.banner-btn {
|
||||
padding: 6px 14px; border-radius: 7px; font-size: 12px; font-weight: 600; border: none; cursor: pointer; white-space: nowrap; transition: opacity 0.15s;
|
||||
}
|
||||
.banner-btn:hover { opacity: 0.85; }
|
||||
.banner-btn.blue-btn { background: var(--info); color: #fff; }
|
||||
.banner-btn.green-btn { background: var(--accent); color: #fff; }
|
||||
|
||||
.chat-area { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 16px; }
|
||||
.msg { display: flex; gap: 10px; max-width: 75%; }
|
||||
.msg.bot { align-self: flex-start; }
|
||||
.msg.user { align-self: flex-end; flex-direction: row-reverse; }
|
||||
.msg-avatar {
|
||||
width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
|
||||
display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700;
|
||||
}
|
||||
.bot .msg-avatar { background: rgba(4,120,87,0.2); color: var(--accent2); }
|
||||
.user .msg-avatar { background: rgba(59,130,246,0.2); color: var(--info); }
|
||||
.msg-bubble { padding: 10px 14px; border-radius: 12px; font-size: 13px; line-height: 1.6; }
|
||||
.bot .msg-bubble { background: var(--bg2); border: 1px solid var(--border2); border-radius: 2px 12px 12px 12px; }
|
||||
.user .msg-bubble { background: rgba(4,120,87,0.15); border: 1px solid rgba(4,120,87,0.25); border-radius: 12px 2px 12px 12px; }
|
||||
.msg-time { font-size: 10px; color: var(--muted); margin-top: 4px; text-align: right; }
|
||||
.typing { display: flex; gap: 4px; align-items: center; padding: 6px 0; }
|
||||
.typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); animation: blink 1.2s infinite; }
|
||||
.typing span:nth-child(2) { animation-delay: 0.2s; }
|
||||
.typing span:nth-child(3) { animation-delay: 0.4s; }
|
||||
@keyframes blink { 0%,100%{opacity:0.3} 50%{opacity:1} }
|
||||
|
||||
.chat-input-area { padding: 16px 24px; border-top: 1px solid var(--border); background: var(--bg2); display: flex; gap: 10px; align-items: flex-end; }
|
||||
.chat-input {
|
||||
flex: 1; background: var(--bg3); border: 1px solid var(--border2); border-radius: 10px;
|
||||
padding: 10px 14px; color: var(--text); font-size: 13px; font-family: inherit;
|
||||
resize: none; outline: none; min-height: 42px; max-height: 120px; transition: border-color 0.15s;
|
||||
}
|
||||
.chat-input:focus { border-color: rgba(4,120,87,0.5); }
|
||||
.chat-input::placeholder { color: var(--muted); }
|
||||
.send-btn {
|
||||
width: 42px; height: 42px; border-radius: 10px; background: var(--accent); border: none; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center; color: #fff; font-size: 16px; transition: background 0.15s;
|
||||
}
|
||||
.send-btn:hover { background: var(--accent2); }
|
||||
|
||||
/* ─── SCREEN 2: ADMIN ─── */
|
||||
.admin-view { padding: 28px 32px; flex: 1; }
|
||||
.page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; }
|
||||
.page-title h2 { font-size: 20px; font-weight: 700; }
|
||||
.page-title p { font-size: 13px; color: var(--muted); margin-top: 2px; }
|
||||
.page-actions { display: flex; gap: 10px; }
|
||||
.btn { padding: 8px 16px; border-radius: 8px; font-size: 13px; font-weight: 600; cursor: pointer; border: none; transition: all 0.15s; }
|
||||
.btn-green { background: var(--accent); color: #fff; }
|
||||
.btn-green:hover { background: var(--accent2); }
|
||||
.btn-ghost { background: transparent; color: var(--muted); border: 1px solid var(--border2); }
|
||||
.btn-ghost:hover { color: var(--text); border-color: var(--muted); }
|
||||
|
||||
.stats-row { display: flex; gap: 16px; margin-bottom: 28px; flex-wrap: wrap; }
|
||||
.stat-card {
|
||||
background: var(--bg2); border: 1px solid var(--border); border-radius: 12px;
|
||||
padding: 18px 20px; flex: 1; min-width: 130px;
|
||||
}
|
||||
.stat-card .label { font-size: 12px; color: var(--muted); font-weight: 500; }
|
||||
.stat-card .value { font-family: 'Montserrat', sans-serif; font-size: 26px; font-weight: 800; margin-top: 4px; }
|
||||
.stat-card .change { font-size: 11px; color: var(--accent3); margin-top: 2px; }
|
||||
|
||||
.clients-table { background: var(--bg2); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; }
|
||||
.table-head { display: grid; grid-template-columns: 2fr 1.2fr 1.2fr 1fr 1fr 1.2fr; padding: 12px 20px; border-bottom: 1px solid var(--border); background: var(--bg3); }
|
||||
.table-head span { font-size: 11px; text-transform: uppercase; letter-spacing: 0.8px; color: var(--muted); font-weight: 600; }
|
||||
.table-row { display: grid; grid-template-columns: 2fr 1.2fr 1.2fr 1fr 1fr 1.2fr; padding: 14px 20px; border-bottom: 1px solid var(--border); align-items: center; transition: background 0.15s; }
|
||||
.table-row:last-child { border-bottom: none; }
|
||||
.table-row:hover { background: rgba(255,255,255,0.02); }
|
||||
.client-name-cell { display: flex; flex-direction: column; }
|
||||
.client-name { font-size: 14px; font-weight: 600; }
|
||||
.client-company { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
||||
.priority { display: flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; }
|
||||
.priority-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||
.p-high .priority-dot { background: var(--danger); }
|
||||
.p-high { color: var(--danger); }
|
||||
.p-mid .priority-dot { background: var(--warning); }
|
||||
.p-mid { color: var(--warning); }
|
||||
.p-low .priority-dot { background: var(--accent2); }
|
||||
.p-low { color: var(--accent2); }
|
||||
.phase-badge { padding: 3px 10px; border-radius: 100px; font-size: 11px; font-weight: 600; display: inline-flex; }
|
||||
.phase-be { background: rgba(59,130,246,0.12); color: #60A5FA; border: 1px solid rgba(59,130,246,0.2); }
|
||||
.phase-asis { background: rgba(239,68,68,0.12); color: #F87171; border: 1px solid rgba(239,68,68,0.2); }
|
||||
.phase-audit { background: rgba(245,158,11,0.12); color: #FCD34D; border: 1px solid rgba(245,158,11,0.2); }
|
||||
.tobe-badge { background: rgba(4,120,87,0.12); color: var(--accent3); border: 1px solid rgba(4,120,87,0.3); }
|
||||
.row-actions { display: flex; gap: 6px; }
|
||||
.action-btn { padding: 5px 10px; border-radius: 6px; font-size: 11px; font-weight: 600; border: 1px solid var(--border2); background: transparent; color: var(--muted); cursor: pointer; transition: all 0.15s; }
|
||||
.action-btn:hover { color: var(--text); border-color: var(--muted); }
|
||||
.action-btn.primary { background: rgba(4,120,87,0.12); color: var(--accent2); border-color: rgba(4,120,87,0.3); }
|
||||
.action-btn.primary:hover { background: var(--accent); color: #fff; }
|
||||
|
||||
/* ─── SCREEN 3: CLIENT CARD ─── */
|
||||
.card-view { padding: 28px 32px; flex: 1; }
|
||||
.card-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 28px; flex-wrap: wrap; gap: 16px; }
|
||||
.card-title-block h2 { font-size: 22px; font-weight: 700; }
|
||||
.card-title-block p { font-size: 13px; color: var(--muted); margin-top: 4px; }
|
||||
.card-tags { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
|
||||
.tag { padding: 4px 12px; border-radius: 100px; font-size: 12px; font-weight: 600; border: 1px solid; }
|
||||
.tag-green { background: rgba(4,120,87,0.12); color: var(--accent3); border-color: rgba(4,120,87,0.3); }
|
||||
.tag-blue { background: rgba(59,130,246,0.1); color: #60A5FA; border-color: rgba(59,130,246,0.25); }
|
||||
.tag-orange { background: rgba(249,115,22,0.1); color: #FB923C; border-color: rgba(249,115,22,0.25); }
|
||||
|
||||
.card-grid { display: grid; grid-template-columns: 1fr 300px; gap: 20px; }
|
||||
.card-main { display: flex; flex-direction: column; gap: 20px; }
|
||||
.card-side { display: flex; flex-direction: column; gap: 16px; }
|
||||
|
||||
.card-section { background: var(--bg2); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; }
|
||||
.section-head { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
|
||||
.section-head h3 { font-size: 13px; font-weight: 700; }
|
||||
.section-body { padding: 18px; }
|
||||
|
||||
.funnel-steps { display: flex; gap: 6px; }
|
||||
.funnel-pill {
|
||||
flex: 1; text-align: center; padding: 8px 6px; border-radius: 8px;
|
||||
font-size: 11px; font-weight: 600; border: 1px solid var(--border); color: var(--muted);
|
||||
cursor: pointer; transition: all 0.15s; position: relative;
|
||||
}
|
||||
.funnel-pill.done { background: rgba(4,120,87,0.08); color: var(--accent2); border-color: rgba(4,120,87,0.25); }
|
||||
.funnel-pill.current { background: rgba(4,120,87,0.2); color: var(--accent3); border-color: var(--accent2); box-shadow: 0 0 12px rgba(4,120,87,0.2); }
|
||||
.funnel-pill.done::after { content: '✓'; position: absolute; top: -5px; right: -3px; background: var(--accent); color: #fff; font-size: 9px; width: 14px; height: 14px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
|
||||
|
||||
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.info-item label { font-size: 11px; color: var(--muted); font-weight: 500; display: block; margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.5px; }
|
||||
.info-item .val { font-size: 14px; font-weight: 500; }
|
||||
|
||||
.brief-items { display: flex; flex-direction: column; gap: 12px; }
|
||||
.brief-item { background: var(--bg3); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; }
|
||||
.brief-item .brief-q { font-size: 11px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
|
||||
.brief-item .brief-a { font-size: 13px; line-height: 1.5; }
|
||||
|
||||
.timeline { display: flex; flex-direction: column; gap: 0; }
|
||||
.tl-item { display: flex; gap: 12px; padding-bottom: 16px; position: relative; }
|
||||
.tl-item:not(:last-child)::before { content: ''; position: absolute; left: 11px; top: 24px; bottom: 0; width: 1px; background: var(--border); }
|
||||
.tl-dot { width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 11px; margin-top: 2px; }
|
||||
.tl-green { background: rgba(4,120,87,0.2); color: var(--accent2); }
|
||||
.tl-blue { background: rgba(59,130,246,0.15); color: #60A5FA; }
|
||||
.tl-yellow { background: rgba(245,158,11,0.15); color: #FCD34D; }
|
||||
.tl-content p { font-size: 13px; line-height: 1.5; }
|
||||
.tl-content .tl-time { font-size: 11px; color: var(--muted); margin-top: 2px; }
|
||||
|
||||
.side-card { background: var(--bg2); border: 1px solid var(--border); border-radius: 14px; padding: 16px; }
|
||||
.side-card h4 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; color: var(--muted); margin-bottom: 12px; }
|
||||
.side-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
|
||||
.side-row:last-child { border-bottom: none; }
|
||||
.side-row .sl { color: var(--muted); }
|
||||
.side-row .sv { font-weight: 600; }
|
||||
.prog-bar { height: 6px; border-radius: 4px; background: var(--border); overflow: hidden; margin-top: 8px; }
|
||||
.prog-fill { height: 100%; border-radius: 4px; background: linear-gradient(90deg, var(--accent) 0%, var(--accent2) 100%); }
|
||||
|
||||
/* ─── SCREEN 4: INTERVIEW ─── */
|
||||
.interview-view { padding: 28px 32px; flex: 1; max-width: 800px; }
|
||||
.interview-header { margin-bottom: 28px; }
|
||||
.interview-header h2 { font-size: 20px; font-weight: 700; }
|
||||
.interview-header p { font-size: 13px; color: var(--muted); margin-top: 4px; }
|
||||
.progress-block { background: var(--bg2); border: 1px solid var(--border); border-radius: 12px; padding: 16px 20px; margin-bottom: 24px; }
|
||||
.prog-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
|
||||
.prog-label { font-size: 13px; font-weight: 600; }
|
||||
.prog-pct { font-size: 13px; color: var(--accent2); font-weight: 700; }
|
||||
.steps-row { display: flex; gap: 6px; margin-top: 12px; }
|
||||
.step-pill { flex: 1; height: 6px; border-radius: 4px; background: var(--border); }
|
||||
.step-pill.done { background: var(--accent); }
|
||||
.step-pill.active { background: var(--accent2); animation: pulse 1.5s infinite; }
|
||||
@keyframes pulse { 0%,100%{opacity:0.7} 50%{opacity:1} }
|
||||
|
||||
.q-section { display: flex; flex-direction: column; gap: 16px; }
|
||||
.q-card { background: var(--bg2); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; }
|
||||
.q-card.answered { border-color: rgba(4,120,87,0.3); }
|
||||
.q-card-head { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
|
||||
.q-num { font-size: 11px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.8px; }
|
||||
.answered-check { background: rgba(4,120,87,0.15); color: var(--accent2); padding: 3px 10px; border-radius: 100px; font-size: 11px; font-weight: 700; }
|
||||
.q-body { padding: 16px 18px; }
|
||||
.q-text { font-size: 15px; font-weight: 600; margin-bottom: 12px; line-height: 1.4; }
|
||||
.q-hint { font-size: 12px; color: var(--muted); margin-bottom: 14px; line-height: 1.5; }
|
||||
.q-answer { background: rgba(4,120,87,0.06); border: 1px solid rgba(4,120,87,0.2); border-radius: 8px; padding: 10px 14px; font-size: 13px; color: var(--text); line-height: 1.6; }
|
||||
.q-textarea {
|
||||
width: 100%; background: var(--bg3); border: 1px solid var(--border2); border-radius: 10px;
|
||||
padding: 12px 14px; color: var(--text); font-size: 13px; font-family: inherit;
|
||||
min-height: 90px; resize: vertical; outline: none; transition: border-color 0.15s;
|
||||
}
|
||||
.q-textarea:focus { border-color: rgba(4,120,87,0.5); }
|
||||
.q-footer { padding: 12px 18px; border-top: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; }
|
||||
.q-footer-right { display: flex; gap: 8px; }
|
||||
.voice-btn { padding: 7px 14px; border-radius: 8px; background: rgba(59,130,246,0.12); color: #60A5FA; border: 1px solid rgba(59,130,246,0.25); font-size: 12px; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 6px; }
|
||||
.save-btn { padding: 7px 14px; border-radius: 8px; background: var(--accent); color: #fff; border: none; font-size: 12px; font-weight: 600; cursor: pointer; }
|
||||
.save-btn:hover { background: var(--accent2); }
|
||||
|
||||
/* scrollbar */
|
||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--muted); }
|
||||
|
||||
@media(max-width: 768px) {
|
||||
.ws-sidebar { display: none; }
|
||||
.card-grid { grid-template-columns: 1fr; }
|
||||
.table-head, .table-row { grid-template-columns: 2fr 1fr 1fr; }
|
||||
.table-head span:nth-child(n+4), .table-row > *:nth-child(n+4) { display: none; }
|
||||
.admin-view, .card-view, .interview-view { padding: 16px; }
|
||||
.card-view { padding: 16px; }
|
||||
.funnel-steps { flex-wrap: wrap; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- TOP NAV -->
|
||||
<nav class="top-nav">
|
||||
<div class="nav-logo">@wasrusgen<span>1</span></div>
|
||||
<div class="tab-bar">
|
||||
<div class="tab active" onclick="showScreen(0, this)">💬 Workspace клиента</div>
|
||||
<div class="tab" onclick="showScreen(1, this)">🗂️ Admin панель</div>
|
||||
<div class="tab" onclick="showScreen(2, this)">👤 Карточка клиента</div>
|
||||
<div class="tab" onclick="showScreen(3, this)">📋 Интервью</div>
|
||||
</div>
|
||||
<div class="nav-badge">Demo Mode</div>
|
||||
</nav>
|
||||
|
||||
<!-- ─── SCREEN 1: WORKSPACE ─── -->
|
||||
<div class="screen active" id="screen-0">
|
||||
<div class="workspace">
|
||||
<div class="ws-sidebar">
|
||||
<h3>Навигация</h3>
|
||||
<div class="sidebar-nav">
|
||||
<div class="sidebar-item active">
|
||||
<span class="icon">💬</span> Чат с Еленой
|
||||
</div>
|
||||
<div class="sidebar-item">
|
||||
<span class="icon">📝</span> Первичный бриф
|
||||
</div>
|
||||
<div class="sidebar-item">
|
||||
<span class="icon">🎙️</span> Интервью
|
||||
</div>
|
||||
<div class="sidebar-item">
|
||||
<span class="icon">📄</span> Документы
|
||||
</div>
|
||||
</div>
|
||||
<h3>Текущий проект</h3>
|
||||
<div style="background:var(--bg3);border:1px solid var(--border);border-radius:10px;padding:14px 12px;">
|
||||
<div style="font-size:13px;font-weight:600;margin-bottom:6px;">ООО «ШвейМастер»</div>
|
||||
<div style="font-size:11px;color:var(--muted);margin-bottom:10px;">Швейное производство · Самара</div>
|
||||
<div style="font-size:11px;font-weight:600;color:var(--accent2);">Фаза: TO-BE</div>
|
||||
<div class="prog-bar" style="margin-top:8px;"><div class="prog-fill" style="width:60%"></div></div>
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:4px;">3 из 5 этапов завершены</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ws-main">
|
||||
<div class="ws-header">
|
||||
<div class="ws-header-left">
|
||||
<h2>AI-ассистент Елена</h2>
|
||||
<p>Помогает структурировать информацию и сопровождает на всех этапах</p>
|
||||
</div>
|
||||
<div class="status-badge status-active">● Онлайн</div>
|
||||
</div>
|
||||
<div class="banners">
|
||||
<div class="banner blue">
|
||||
<div class="banner-info">
|
||||
<div class="banner-dot"></div>
|
||||
<div>
|
||||
<div class="banner-title">Первичный бриф — 3/5 блоков заполнено</div>
|
||||
<div class="banner-sub">Осталось заполнить блоки «Цели» и «Финансы»</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="banner-btn blue-btn">Продолжить</button>
|
||||
</div>
|
||||
<div class="banner green">
|
||||
<div class="banner-info">
|
||||
<div class="banner-dot"></div>
|
||||
<div>
|
||||
<div class="banner-title">Интервью доступно — 8 вопросов</div>
|
||||
<div class="banner-sub">Отвечено 2 из 8 · ~20 минут</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="banner-btn green-btn">Пройти интервью</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-area" id="chatArea">
|
||||
<div class="msg bot">
|
||||
<div class="msg-avatar">Е</div>
|
||||
<div>
|
||||
<div class="msg-bubble">Добрый день! Я Елена, ваш AI-ассистент в рамках проекта «Оптимизация швейного производства». Мы находимся на этапе TO-BE — проектирования целевого состояния. Вы уже заполнили 3 блока первичного брифа. Готовы продолжить?</div>
|
||||
<div class="msg-time">10:42</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="msg user">
|
||||
<div class="msg-avatar">В</div>
|
||||
<div>
|
||||
<div class="msg-bubble">Да, давай продолжим. Сегодня хочу разобраться с целевыми показателями.</div>
|
||||
<div class="msg-time">10:44</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="msg bot">
|
||||
<div class="msg-avatar">Е</div>
|
||||
<div>
|
||||
<div class="msg-bubble">Отлично. Для блока «Цели» мне нужно понять: какой результат для вас будет считаться успехом через 6 месяцев после внедрения? Конкретные метрики — производительность, количество позиций, сроки пошива. Можете описать?</div>
|
||||
<div class="msg-time">10:44</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="msg user">
|
||||
<div class="msg-avatar">В</div>
|
||||
<div>
|
||||
<div class="msg-bubble">Хочу увеличить выпуск с 200 до 350 единиц в день, снизить брак с 8% до 3%, и уложить пошив стандартного изделия в 45 минут вместо нынешних 70.</div>
|
||||
<div class="msg-time">10:47</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="msg bot">
|
||||
<div class="msg-avatar">Е</div>
|
||||
<div>
|
||||
<div class="msg-bubble">Зафиксировала. Целевые показатели TO-BE:<br>• Выпуск: 200 → 350 ед/день (+75%)<br>• Брак: 8% → 3% (сокращение в 2.7 раза)<br>• Цикл пошива: 70 → 45 мин (-36%)<br><br>Эти данные автоматически добавлены в карточку проекта. Переходим к блоку «Финансы»?</div>
|
||||
<div class="msg-time">10:47</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input-area">
|
||||
<textarea class="chat-input" placeholder="Напишите сообщение Елене..." rows="1"></textarea>
|
||||
<button class="send-btn">➤</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─── SCREEN 2: ADMIN ─── -->
|
||||
<div class="screen" id="screen-1">
|
||||
<div class="admin-view">
|
||||
<div class="page-header">
|
||||
<div class="page-title">
|
||||
<h2>Клиенты</h2>
|
||||
<p>Управление проектами · 3 активных клиента</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-ghost">Фильтр</button>
|
||||
<button class="btn btn-green">+ Новый клиент</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats-row">
|
||||
<div class="stat-card">
|
||||
<div class="label">Всего клиентов</div>
|
||||
<div class="value">3</div>
|
||||
<div class="change">↑ +1 за месяц</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Активных проектов</div>
|
||||
<div class="value">3</div>
|
||||
<div class="change">в работе</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Ср. этап воронки</div>
|
||||
<div class="value" style="color:var(--accent2)">TO-BE</div>
|
||||
<div class="change">3 из 5 этапов</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Документов готово</div>
|
||||
<div class="value">5</div>
|
||||
<div class="change">ТЗ, договоры</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clients-table">
|
||||
<div class="table-head">
|
||||
<span>Клиент / Компания</span>
|
||||
<span>Приоритет</span>
|
||||
<span>Фаза</span>
|
||||
<span>Бриф</span>
|
||||
<span>Интервью</span>
|
||||
<span>Действия</span>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="client-name-cell">
|
||||
<span class="client-name">Васильев Роман</span>
|
||||
<span class="client-company">ООО «ШвейМастер» · Самара</span>
|
||||
</div>
|
||||
<div class="priority p-high"><div class="priority-dot"></div>Высокий</div>
|
||||
<div><span class="phase-badge tobe-badge">TO-BE</span></div>
|
||||
<div style="font-size:13px;color:var(--accent2);font-weight:600;">3/5 ✓</div>
|
||||
<div style="font-size:13px;color:var(--warning);font-weight:600;">2/8</div>
|
||||
<div class="row-actions">
|
||||
<button class="action-btn primary">Открыть</button>
|
||||
<button class="action-btn">Интервью</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="client-name-cell">
|
||||
<span class="client-name">Крылова Марина</span>
|
||||
<span class="client-company">ИП Крылова · Краснодар</span>
|
||||
</div>
|
||||
<div class="priority p-mid"><div class="priority-dot"></div>Средний</div>
|
||||
<div><span class="phase-badge phase-audit">Аудит</span></div>
|
||||
<div style="font-size:13px;color:var(--warning);font-weight:600;">2/5</div>
|
||||
<div style="font-size:13px;color:var(--muted);">—</div>
|
||||
<div class="row-actions">
|
||||
<button class="action-btn primary">Открыть</button>
|
||||
<button class="action-btn">Интервью</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="client-name-cell">
|
||||
<span class="client-name">Ломов Дмитрий</span>
|
||||
<span class="client-company">ООО «ТекстильПро» · Москва</span>
|
||||
</div>
|
||||
<div class="priority p-low"><div class="priority-dot"></div>Обычный</div>
|
||||
<div><span class="phase-badge phase-asis">AS-IS</span></div>
|
||||
<div style="font-size:13px;color:var(--danger);font-weight:600;">0/5</div>
|
||||
<div style="font-size:13px;color:var(--muted);">—</div>
|
||||
<div class="row-actions">
|
||||
<button class="action-btn primary">Открыть</button>
|
||||
<button class="action-btn">Бриф</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─── SCREEN 3: CLIENT CARD ─── -->
|
||||
<div class="screen" id="screen-2">
|
||||
<div class="card-view">
|
||||
<div class="card-header">
|
||||
<div class="card-title-block">
|
||||
<h2>Васильев Роман · ООО «ШвейМастер»</h2>
|
||||
<p>Швейное производство, Самара · Создан 14.03.2025</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag tag-green">Лид: Активен</span>
|
||||
<span class="tag tag-blue">TO-BE</span>
|
||||
<span class="tag tag-orange">Приоритет: Высокий</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||
<button class="btn btn-ghost" style="font-size:12px;">📄 Договор</button>
|
||||
<button class="btn btn-ghost" style="font-size:12px;">📋 ТЗ</button>
|
||||
<button class="btn btn-green" style="font-size:12px;">Запустить интервью</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-grid">
|
||||
<div class="card-main">
|
||||
|
||||
<!-- Воронка -->
|
||||
<div class="card-section">
|
||||
<div class="section-head">
|
||||
<h3>Воронка проекта</h3>
|
||||
<span style="font-size:12px;color:var(--accent2);font-weight:600;">Этап 3/5</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="funnel-steps">
|
||||
<div class="funnel-pill done">AS-IS</div>
|
||||
<div class="funnel-pill done">Аудит</div>
|
||||
<div class="funnel-pill current">TO-BE</div>
|
||||
<div class="funnel-pill">Внедрение</div>
|
||||
<div class="funnel-pill">Сопровождение</div>
|
||||
</div>
|
||||
<div style="margin-top:16px;padding:12px 14px;background:var(--bg3);border-radius:8px;border:1px solid rgba(4,120,87,0.2);">
|
||||
<div style="font-size:12px;font-weight:700;color:var(--accent2);margin-bottom:6px;">TO-BE — Проектирование целевого состояния</div>
|
||||
<div style="font-size:13px;color:var(--muted);">Определение целевых показателей, разработка новых процессов, согласование ресурсов и сроков внедрения.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Бриф -->
|
||||
<div class="card-section">
|
||||
<div class="section-head">
|
||||
<h3>Первичный бриф</h3>
|
||||
<span style="font-size:12px;color:var(--warning);font-weight:600;">3/5 заполнено</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="brief-items">
|
||||
<div class="brief-item">
|
||||
<div class="brief-q">📌 Текущая ситуация</div>
|
||||
<div class="brief-a">Швейный цех в Самаре, 45 сотрудников. Выпуск: 200 единиц/день (джинсы, платья). Основные проблемы: высокий % брака (8%), долгий цикл пошива (70 мин), неравномерная загрузка швей.</div>
|
||||
</div>
|
||||
<div class="brief-item">
|
||||
<div class="brief-q">🏢 Структура бизнеса</div>
|
||||
<div class="brief-a">ООО «ШвейМастер», основан 2018. Прямые продажи + маркетплейсы (Wildberries 60%). Оборот ~18 млн руб/год. Есть технолог, нет операционного директора.</div>
|
||||
</div>
|
||||
<div class="brief-item">
|
||||
<div class="brief-q">⚡ Ключевые боли</div>
|
||||
<div class="brief-a">Брак 8% = потери ~1.4 млн руб/год. Простои 30% рабочего времени. Нет системы учёта операций — всё в Excel и голове технолога.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- История -->
|
||||
<div class="card-section">
|
||||
<div class="section-head"><h3>История взаимодействий</h3></div>
|
||||
<div class="section-body">
|
||||
<div class="timeline">
|
||||
<div class="tl-item">
|
||||
<div class="tl-dot tl-green">✓</div>
|
||||
<div class="tl-content">
|
||||
<p><strong>Завершён этап AS-IS</strong> — зафиксировано 12 ключевых процессов производства</p>
|
||||
<div class="tl-time">18 марта 2025, 14:30</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tl-item">
|
||||
<div class="tl-dot tl-blue">📋</div>
|
||||
<div class="tl-content">
|
||||
<p><strong>Заполнен бриф (блок 3/5)</strong> — добавлены ключевые боли и цели проекта</p>
|
||||
<div class="tl-time">22 марта 2025, 11:15</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tl-item">
|
||||
<div class="tl-dot tl-yellow">🎙️</div>
|
||||
<div class="tl-content">
|
||||
<p><strong>Проведено интервью (2/8)</strong> — голосовой ввод, расшифрован и структурирован</p>
|
||||
<div class="tl-time">25 марта 2025, 09:50</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tl-item">
|
||||
<div class="tl-dot tl-green">✓</div>
|
||||
<div class="tl-content">
|
||||
<p><strong>Зафиксированы целевые показатели TO-BE</strong> — производительность +75%, брак до 3%</p>
|
||||
<div class="tl-time">28 мая 2025, 10:47</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SIDE -->
|
||||
<div class="card-side">
|
||||
<div class="side-card">
|
||||
<h4>Контакт</h4>
|
||||
<div class="side-row"><span class="sl">Имя</span><span class="sv">Васильев Роман</span></div>
|
||||
<div class="side-row"><span class="sl">Компания</span><span class="sv">ООО «ШвейМастер»</span></div>
|
||||
<div class="side-row"><span class="sl">Телефон</span><span class="sv">+7 846 XXX-XX-XX</span></div>
|
||||
<div class="side-row"><span class="sl">Email</span><span class="sv">roman@sh-master.ru</span></div>
|
||||
<div class="side-row"><span class="sl">Город</span><span class="sv">Самара</span></div>
|
||||
</div>
|
||||
<div class="side-card">
|
||||
<h4>Прогресс проекта</h4>
|
||||
<div class="side-row"><span class="sl">Бриф</span><span class="sv" style="color:var(--warning)">3/5</span></div>
|
||||
<div class="prog-bar"><div class="prog-fill" style="width:60%"></div></div>
|
||||
<div class="side-row" style="margin-top:10px;"><span class="sl">Интервью</span><span class="sv" style="color:var(--warning)">2/8</span></div>
|
||||
<div class="prog-bar"><div class="prog-fill" style="width:25%"></div></div>
|
||||
<div class="side-row" style="margin-top:10px;"><span class="sl">Документы</span><span class="sv" style="color:var(--accent2)">2 готово</span></div>
|
||||
</div>
|
||||
<div class="side-card">
|
||||
<h4>Документы</h4>
|
||||
<div class="side-row">
|
||||
<span class="sl">📄 Договор</span>
|
||||
<span class="sv" style="color:var(--accent2);font-size:12px;">Готов</span>
|
||||
</div>
|
||||
<div class="side-row">
|
||||
<span class="sl">📋 ТЗ v1</span>
|
||||
<span class="sv" style="color:var(--accent2);font-size:12px;">Черновик</span>
|
||||
</div>
|
||||
<div class="side-row">
|
||||
<span class="sl">📊 Отчёт AS-IS</span>
|
||||
<span class="sv" style="color:var(--accent2);font-size:12px;">Готов</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─── SCREEN 4: INTERVIEW ─── -->
|
||||
<div class="screen" id="screen-3">
|
||||
<div class="interview-view">
|
||||
<div class="interview-header">
|
||||
<h2>Интервью: ООО «ШвейМастер»</h2>
|
||||
<p>Структурированный опрос для этапа TO-BE · Клиент: Васильев Роман</p>
|
||||
</div>
|
||||
|
||||
<div class="progress-block">
|
||||
<div class="prog-header">
|
||||
<span class="prog-label">Прогресс интервью</span>
|
||||
<span class="prog-pct">25% — 2 из 8 вопросов</span>
|
||||
</div>
|
||||
<div class="prog-bar"><div class="prog-fill" style="width:25%"></div></div>
|
||||
<div class="steps-row">
|
||||
<div class="step-pill done"></div>
|
||||
<div class="step-pill done"></div>
|
||||
<div class="step-pill active"></div>
|
||||
<div class="step-pill"></div>
|
||||
<div class="step-pill"></div>
|
||||
<div class="step-pill"></div>
|
||||
<div class="step-pill"></div>
|
||||
<div class="step-pill"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="q-section">
|
||||
<!-- Отвеченный вопрос -->
|
||||
<div class="q-card answered">
|
||||
<div class="q-card-head">
|
||||
<span class="q-num">Вопрос 1 из 8</span>
|
||||
<span class="answered-check">✓ Отвечено</span>
|
||||
</div>
|
||||
<div class="q-body">
|
||||
<div class="q-text">Опишите ваш текущий производственный поток: от получения ткани до отгрузки готовых изделий. Какие ключевые этапы и сколько времени занимает каждый?</div>
|
||||
<div class="q-answer">Получение и складирование ткани → 1 день. Раскрой — 2-3 часа на партию 50 изделий. Пошив — в среднем 70 минут на изделие (зависит от модели). ОТК и упаковка — 15 минут. Итого от раскроя до готового изделия — примерно 1.5-2 смены.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ещё один отвеченный -->
|
||||
<div class="q-card answered">
|
||||
<div class="q-card-head">
|
||||
<span class="q-num">Вопрос 2 из 8</span>
|
||||
<span class="answered-check">✓ Отвечено</span>
|
||||
</div>
|
||||
<div class="q-body">
|
||||
<div class="q-text">Какие операции в процессе пошива являются «узкими местами»? Где скапливаются незавершённые изделия?</div>
|
||||
<div class="q-answer">Главное узкое место — установка молний и фурнитуры. Там 3 швеи обрабатывают то, что приходит от 12 швей на предыдущем этапе. Ещё скопление перед ОТК — контролёр один на весь цех, проверяет медленно.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Активный вопрос -->
|
||||
<div class="q-card">
|
||||
<div class="q-card-head">
|
||||
<span class="q-num">Вопрос 3 из 8 — Текущий</span>
|
||||
</div>
|
||||
<div class="q-body">
|
||||
<div class="q-text">Как сейчас организовано планирование производства? Кто принимает решения о приоритетах заказов и распределении задач по швеям?</div>
|
||||
<div class="q-hint">Расскажите как есть, без прикрас. Важно понять реальный процесс, а не идеальный. Можно упомянуть конкретных сотрудников и их роли.</div>
|
||||
<textarea class="q-textarea" placeholder="Напишите или надиктуйте ответ..."></textarea>
|
||||
</div>
|
||||
<div class="q-footer">
|
||||
<span style="font-size:12px;color:var(--muted);">Можно отвечать голосом</span>
|
||||
<div class="q-footer-right">
|
||||
<button class="voice-btn">🎙️ Голос</button>
|
||||
<button class="save-btn">Сохранить и дальше →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Заблокированный -->
|
||||
<div class="q-card" style="opacity:0.5;">
|
||||
<div class="q-card-head">
|
||||
<span class="q-num">Вопрос 4 из 8</span>
|
||||
<span style="font-size:11px;color:var(--muted);">🔒 Ответьте на вопрос 3</span>
|
||||
</div>
|
||||
<div class="q-body">
|
||||
<div class="q-text">Какие метрики и KPI вы используете для оценки работы цеха?</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showScreen(idx, tab) {
|
||||
document.querySelectorAll('.screen').forEach((s, i) => s.classList.toggle('active', i === idx));
|
||||
document.querySelectorAll('.tab').forEach((t, i) => t.classList.toggle('active', i === idx));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
981
Mokap/consult_demo_card.html
Normal file
981
Mokap/consult_demo_card.html
Normal file
@ -0,0 +1,981 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{{code}} · @wasrusgen1 CRM</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{font-family:'Inter',sans-serif;background:#F5F6F8;color:#1A1A2E;height:100vh;overflow:hidden}
|
||||
|
||||
/* ── Layout shell (same as admin) ── */
|
||||
.layout{display:flex;height:100vh}
|
||||
.sidebar{width:220px;background:#0F0F1A;display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}
|
||||
.sb-top{padding:20px 18px 16px;border-bottom:1px solid rgba(255,255,255,.07)}
|
||||
.sb-brand{font-family:'Montserrat',sans-serif;font-size:13px;font-weight:800;color:#fff;letter-spacing:.04em;display:flex;align-items:center;gap:8px}
|
||||
.sb-sub{font-size:10px;color:rgba(255,255,255,.35);margin-top:3px;letter-spacing:.02em}
|
||||
.sb-nav{flex:1;overflow-y:auto;padding:12px 0;scrollbar-width:none}
|
||||
.sb-nav::-webkit-scrollbar{display:none}
|
||||
.nav-section{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.12em;color:rgba(255,255,255,.25);padding:12px 18px 4px}
|
||||
.nav-item{display:flex;align-items:center;gap:9px;padding:8px 18px;cursor:pointer;font-size:13px;color:rgba(255,255,255,.55);border:none;background:none;width:100%;text-align:left;font-family:'Inter',sans-serif;transition:.15s;text-decoration:none}
|
||||
.nav-item:hover{color:rgba(255,255,255,.85);background:rgba(255,255,255,.05)}
|
||||
.nav-item.active{color:#fff;background:rgba(4,120,87,.12);border-left:2px solid #047857;padding-left:16px}
|
||||
.ni-icon{font-size:14px;flex-shrink:0;width:18px;text-align:center}
|
||||
.ni-label{flex:1}
|
||||
.sb-client-box{margin:12px 14px 0;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.08);border-radius:10px;padding:10px 12px}
|
||||
.sb-client-code{font-family:monospace;font-size:15px;font-weight:700;color:#10B981;letter-spacing:.06em}
|
||||
.sb-client-name{font-size:11px;color:rgba(255,255,255,.45);margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.sb-footer{padding:14px 18px;border-top:1px solid rgba(255,255,255,.07)}
|
||||
.sb-stat{font-size:11px;color:rgba(255,255,255,.4);line-height:1.8}
|
||||
.sb-stat b{color:rgba(255,255,255,.75);font-size:12px}
|
||||
|
||||
/* ── Content area ── */
|
||||
.content{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}
|
||||
.content-hdr{background:#fff;border-bottom:1.5px solid #E5E7EB;padding:0 28px;height:56px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;gap:16px}
|
||||
.breadcrumb{display:flex;align-items:center;gap:8px;font-size:13px;color:#94A3B8}
|
||||
.breadcrumb a{color:#94A3B8;text-decoration:none;transition:.15s}
|
||||
.breadcrumb a:hover{color:#047857}
|
||||
.breadcrumb-sep{color:#CBD5E1}
|
||||
.breadcrumb-cur{font-weight:700;color:#0f172a;font-family:monospace;font-size:14px}
|
||||
.hdr-actions{display:flex;align-items:center;gap:10px}
|
||||
.btn-save{background:linear-gradient(135deg,#064E3B,#047857);color:#fff;border:none;padding:8px 22px;border-radius:8px;font-family:'Montserrat',sans-serif;font-size:13px;font-weight:700;cursor:pointer;transition:.2s;white-space:nowrap}
|
||||
.btn-save:hover{opacity:.9;transform:translateY(-1px)}
|
||||
.btn-contract{background:#F0FDF4;border:1.5px solid #A7F3D0;color:#047857;padding:7px 14px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:6px;text-decoration:none;transition:.2s;white-space:nowrap}
|
||||
.btn-contract:hover{background:#DCFCE7}
|
||||
.save-status{font-size:12px;color:#94A3B8;transition:.3s;white-space:nowrap}
|
||||
.save-status.ok{color:#059669}
|
||||
.save-status.err{color:#DC2626}
|
||||
.content-body{flex:1;overflow-y:auto;padding:24px 28px 40px}
|
||||
.content-body::-webkit-scrollbar{width:4px}
|
||||
.content-body::-webkit-scrollbar-thumb{background:#E2E8F0;border-radius:4px}
|
||||
|
||||
/* ── Hero card ── */
|
||||
.hero-card{background:#fff;border-radius:14px;border:1.5px solid #E5E7EB;padding:20px 24px;margin-bottom:20px;display:flex;align-items:center;gap:20px;flex-wrap:wrap}
|
||||
.hero-avatar{width:52px;height:52px;border-radius:14px;background:linear-gradient(135deg,#064E3B,#047857);display:flex;align-items:center;justify-content:center;font-family:'Montserrat',sans-serif;font-size:20px;font-weight:800;color:#fff;flex-shrink:0}
|
||||
.hero-main{flex:1;min-width:200px}
|
||||
.hero-name{font-family:'Montserrat',sans-serif;font-size:18px;font-weight:800;color:#0f172a;margin-bottom:4px}
|
||||
.hero-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
||||
.hero-code{font-family:monospace;font-size:11px;color:#94A3B8;background:#F8FAFC;border:1px solid #E2E8F0;padding:2px 8px;border-radius:6px}
|
||||
.hero-stat{font-size:12px;color:#94A3B8}
|
||||
.hero-badges{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:8px}
|
||||
.hero-right{display:flex;flex-direction:column;align-items:flex-end;gap:8px}
|
||||
.type-toggle{display:flex;background:#F1F5F9;border:1.5px solid #E2E8F0;border-radius:8px;padding:3px;gap:2px}
|
||||
.type-btn{padding:5px 14px;border-radius:5px;border:none;font-family:'Inter',sans-serif;font-size:12px;font-weight:600;cursor:pointer;transition:.2s;background:transparent;color:#64748B}
|
||||
.type-btn.active{background:#fff;color:#047857;box-shadow:0 1px 4px rgba(0,0,0,.1)}
|
||||
|
||||
/* ── Tab navigation ── */
|
||||
.tabs-nav{display:flex;gap:2px;background:#fff;border-radius:14px 14px 0 0;border:1.5px solid #E5E7EB;border-bottom:none;padding:6px 6px 0;margin-bottom:0}
|
||||
.tab-btn{padding:9px 18px;border-radius:8px 8px 0 0;border:none;font-family:'Inter',sans-serif;font-size:13px;font-weight:600;cursor:pointer;background:transparent;color:#94A3B8;transition:.15s;display:flex;align-items:center;gap:6px;position:relative;bottom:-1.5px}
|
||||
.tab-btn:hover{color:#475569;background:#F8FAFC}
|
||||
.tab-btn.active{color:#047857;background:#fff;border:1.5px solid #E5E7EB;border-bottom:1.5px solid #fff}
|
||||
.tab-badge{background:#F1F5F9;color:#64748B;border-radius:10px;padding:1px 7px;font-size:10px;font-weight:700}
|
||||
.tab-btn.active .tab-badge{background:#ECFDF5;color:#047857}
|
||||
|
||||
/* ── Tab panes ── */
|
||||
.tab-pane{display:none;background:#fff;border:1.5px solid #E5E7EB;border-radius:0 14px 14px 14px;padding:24px}
|
||||
.tab-pane.active{display:block}
|
||||
|
||||
/* ── Section inside tab ── */
|
||||
.inner-section{margin-bottom:24px}
|
||||
.inner-section:last-child{margin-bottom:0}
|
||||
.inner-title{font-family:'Montserrat',sans-serif;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:#94A3B8;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid #F1F5F9;display:flex;align-items:center;justify-content:space-between}
|
||||
|
||||
/* ── Fields grid ── */
|
||||
.fields{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
|
||||
.fields.cols3{grid-template-columns:repeat(3,1fr)}
|
||||
.fields.cols1{grid-template-columns:1fr}
|
||||
.field{display:flex;flex-direction:column;gap:5px}
|
||||
.field.span2{grid-column:span 2}
|
||||
.field.span3{grid-column:span 3}
|
||||
.field label{font-size:10px;font-weight:600;color:#64748B;text-transform:uppercase;letter-spacing:.06em}
|
||||
.field input,.field textarea,.field select{padding:9px 12px;border:1.5px solid #E2E8F0;border-radius:8px;font-family:'Inter',sans-serif;font-size:13px;outline:none;transition:.2s;background:#fff;color:#0f172a}
|
||||
.field input:focus,.field textarea:focus,.field select:focus{border-color:#047857;box-shadow:0 0 0 3px rgba(4,120,87,.1)}
|
||||
.field input::placeholder,.field textarea::placeholder{color:#CBD5E1}
|
||||
.field textarea{resize:vertical;min-height:72px;line-height:1.5}
|
||||
|
||||
/* ── Collapse (паспорт) ── */
|
||||
.collapse-toggle{display:flex;align-items:center;gap:8px;cursor:pointer;padding:10px 0;font-size:13px;font-weight:600;color:#64748B;border:none;background:none;font-family:'Inter',sans-serif;width:100%;transition:.15s}
|
||||
.collapse-toggle:hover{color:#047857}
|
||||
.collapse-arrow{font-size:10px;transition:transform .2s;color:#94A3B8}
|
||||
.collapse-arrow.open{transform:rotate(90deg)}
|
||||
.collapse-body{display:none;padding-top:14px}
|
||||
.collapse-body.open{display:block}
|
||||
|
||||
/* ── Process flow ── */
|
||||
.phase-flow{display:flex;gap:0;overflow-x:auto;margin-bottom:10px}
|
||||
.phase-bar{height:6px;background:#F5F6F8;border-radius:100px;overflow:hidden;margin-bottom:4px}
|
||||
.phase-bar-fill{height:100%;background:linear-gradient(90deg,#047857,#10B981);border-radius:100px;transition:width .5s ease}
|
||||
.phase-pct-row{display:flex;justify-content:space-between;font-size:10px;color:#94A3B8}
|
||||
|
||||
/* ── Tasks ── */
|
||||
.task-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid #F8FAFC}
|
||||
.task-row:last-child{border-bottom:none}
|
||||
.task-cb{width:15px;height:15px;accent-color:#047857;cursor:pointer;flex-shrink:0}
|
||||
.task-text{flex:1;font-size:13px;color:#0f172a}
|
||||
.task-text.done-text{text-decoration:line-through;color:#94A3B8}
|
||||
.task-prio-tag{font-size:10px;padding:2px 8px;border-radius:10px;flex-shrink:0}
|
||||
.task-del{color:#CBD5E1;font-size:17px;cursor:pointer;line-height:1;border:none;background:none;padding:0 2px}
|
||||
.task-del:hover{color:#DC2626}
|
||||
.task-add-row{display:flex;gap:8px;margin-top:12px}
|
||||
.task-input{flex:1;padding:8px 12px;border:1.5px solid #E2E8F0;border-radius:8px;font-family:'Inter',sans-serif;font-size:13px;outline:none;transition:.2s}
|
||||
.task-input:focus{border-color:#047857;box-shadow:0 0 0 3px rgba(4,120,87,.1)}
|
||||
.task-prio-sel{padding:8px 10px;border:1.5px solid #E2E8F0;border-radius:8px;font-family:'Inter',sans-serif;font-size:12px;outline:none;background:#fff;color:#0f172a;transition:.2s}
|
||||
.btn-add{background:linear-gradient(135deg,#064E3B,#047857);color:#fff;border:none;padding:8px 16px;border-radius:8px;font-family:'Inter',sans-serif;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap}
|
||||
|
||||
/* ── Interview card ── */
|
||||
.iv-empty{font-size:13px;color:#94A3B8;text-align:center;padding:20px 0}
|
||||
.iv-session{border:1.5px solid #E5E7EB;border-radius:12px;overflow:hidden;margin-bottom:14px}
|
||||
.iv-session-hdr{padding:11px 16px;background:#F8FAFC;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;gap:10px}
|
||||
|
||||
/* ── Intake brief ── */
|
||||
.intake-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}
|
||||
.intake-block{border:1.5px solid #E5E7EB;border-radius:10px;padding:14px}
|
||||
.intake-block.empty{opacity:.5;border-color:#F1F5F9}
|
||||
.intake-field-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#94A3B8;margin-bottom:3px}
|
||||
.intake-field-val{font-size:13px;color:#0f172a;background:#F8FAFC;border:1px solid #E2E8F0;border-radius:7px;padding:7px 10px;line-height:1.5}
|
||||
|
||||
/* ── Events ── */
|
||||
.event-row{display:flex;gap:12px;padding:9px 0;border-bottom:1px solid #F8FAFC;align-items:flex-start}
|
||||
.event-row:last-child{border-bottom:none}
|
||||
.event-icon{font-size:17px;flex-shrink:0;margin-top:1px}
|
||||
.event-title{font-size:13px;font-weight:600;color:#0f172a}
|
||||
.event-ts{font-size:11px;color:#94A3B8;margin-top:2px}
|
||||
.note-add-row{display:flex;gap:8px;margin-top:12px}
|
||||
.note-input{flex:1;padding:8px 12px;border:1.5px solid #E2E8F0;border-radius:8px;font-family:'Inter',sans-serif;font-size:13px;outline:none;transition:.2s}
|
||||
.note-input:focus{border-color:#047857;box-shadow:0 0 0 3px rgba(4,120,87,.1)}
|
||||
|
||||
/* ── Modals ── */
|
||||
.modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.52);z-index:1000;align-items:center;justify-content:center}
|
||||
.modal-box{background:#fff;border-radius:16px;padding:28px;width:400px;max-width:92vw;box-shadow:0 20px 60px rgba(0,0,0,.25)}
|
||||
.modal-title{font-family:'Montserrat',sans-serif;font-size:15px;font-weight:800;color:#0f172a;margin-bottom:20px}
|
||||
.modal-footer{display:flex;gap:10px;margin-top:20px}
|
||||
.btn-modal-cancel{flex:1;background:#F1F5F9;border:1.5px solid #E2E8F0;color:#64748B;padding:10px;border-radius:10px;font-family:'Inter',sans-serif;font-size:13px;font-weight:600;cursor:pointer}
|
||||
.btn-modal-ok{flex:2;background:linear-gradient(135deg,#022C22,#047857);color:#fff;border:none;padding:10px;border-radius:10px;font-family:'Inter',sans-serif;font-size:14px;font-weight:700;cursor:pointer}
|
||||
.btn-modal-blue{flex:2;background:linear-gradient(135deg,#1D4ED8,#3B82F6);color:#fff;border:none;padding:10px;border-radius:10px;font-family:'Inter',sans-serif;font-size:14px;font-weight:700;cursor:pointer}
|
||||
.modal-field{margin-bottom:14px}
|
||||
.modal-label{font-size:11px;font-weight:600;color:#64748B;text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:6px}
|
||||
.modal-select,.modal-textarea{width:100%;padding:9px 12px;border:1.5px solid #E2E8F0;border-radius:8px;font-family:'Inter',sans-serif;font-size:13px;outline:none;background:#fff;color:#0f172a;transition:.2s}
|
||||
.modal-select:focus,.modal-textarea:focus{border-color:#047857;box-shadow:0 0 0 3px rgba(4,120,87,.1)}
|
||||
.modal-textarea{resize:vertical;min-height:72px;line-height:1.5}
|
||||
.modal-tip{background:#ECFDF5;border:1px solid #A7F3D0;border-radius:8px;padding:11px;font-size:12px;color:#065F46;margin-bottom:16px}
|
||||
.modal-radio-row{display:flex;gap:8px;margin-top:6px}
|
||||
.modal-radio-lbl{flex:1;border:1.5px solid #E2E8F0;border-radius:8px;padding:9px 11px;cursor:pointer;display:flex;align-items:center;gap:7px;transition:.2s;font-size:13px}
|
||||
|
||||
/* ── Suggest modal ── */
|
||||
.suggest-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:1001;align-items:flex-start;justify-content:center;overflow-y:auto;padding:40px 16px}
|
||||
.suggest-box{background:#fff;border-radius:16px;padding:28px;width:560px;max-width:100%;box-shadow:0 20px 60px rgba(0,0,0,.3);margin:auto}
|
||||
|
||||
/* ── Status / priority badges ── */
|
||||
.badge{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:600}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
|
||||
<!-- ── SIDEBAR ── -->
|
||||
<aside class="sidebar">
|
||||
<div class="sb-top">
|
||||
<div class="sb-brand">
|
||||
<div style="width:28px;height:28px;border-radius:8px;background:#047857;display:flex;align-items:center;justify-content:center;flex-shrink:0">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-family:'Montserrat',sans-serif;font-size:9px;font-weight:500;letter-spacing:.06em;color:rgba(255,255,255,.4);line-height:1;margin-bottom:2px">@wasrusgen1</div>
|
||||
<div style="font-family:'Montserrat',sans-serif;font-size:13px;font-weight:800;color:#fff;letter-spacing:.02em;line-height:1">КОНСАЛТИНГ</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-sub" style="margin-top:4px;padding-left:36px">Admin Panel</div>
|
||||
</div>
|
||||
|
||||
<div class="sb-client-box">
|
||||
<div class="sb-client-code">{{code}}</div>
|
||||
<div class="sb-client-name" id="sb-name">{{name}}</div>
|
||||
</div>
|
||||
|
||||
<nav class="sb-nav">
|
||||
<div class="nav-section">КЛИЕНТЫ</div>
|
||||
<a class="nav-item" href="/consult/admin">
|
||||
<span class="ni-icon">📋</span><span class="ni-label">Все клиенты</span>
|
||||
</a>
|
||||
<div class="nav-section">КАРТОЧКА</div>
|
||||
<a class="nav-item active" href="#" onclick="switchTab('work');return false">
|
||||
<span class="ni-icon">💼</span><span class="ni-label">Работа</span>
|
||||
</a>
|
||||
<a class="nav-item" href="#" onclick="switchTab('interview');return false">
|
||||
<span class="ni-icon">📋</span><span class="ni-label">Интервью</span>
|
||||
</a>
|
||||
<a class="nav-item" href="#" onclick="switchTab('details');return false">
|
||||
<span class="ni-icon">🗂️</span><span class="ni-label">Реквизиты</span>
|
||||
</a>
|
||||
<a class="nav-item" href="#" onclick="switchTab('history');return false">
|
||||
<span class="ni-icon">🕐</span><span class="ni-label">История</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="sb-footer">
|
||||
<div class="sb-stat">Добавлен <b>{{created}}</b></div>
|
||||
<div class="sb-stat"><b>{{msg_count}}</b> сообщений</div>
|
||||
<div class="sb-stat">Визит: <b>{{last_active}}</b></div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- ── CONTENT ── -->
|
||||
<main class="content">
|
||||
<div class="content-hdr">
|
||||
<div class="breadcrumb">
|
||||
<a href="/consult/admin">Клиенты</a>
|
||||
<span class="breadcrumb-sep">›</span>
|
||||
<span class="breadcrumb-cur">{{code}}</span>
|
||||
<span id="hdr-type-chip"></span>
|
||||
</div>
|
||||
<div class="hdr-actions">
|
||||
<span class="save-status" id="save-status"></span>
|
||||
<a href="/consult/admin/client/{{code}}/contract" target="_blank" class="btn-contract">
|
||||
<svg width="13" height="13" 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"/></svg>
|
||||
Договор
|
||||
</a>
|
||||
<button class="btn-save" onclick="saveCard()">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-body">
|
||||
|
||||
<!-- ── HERO ── -->
|
||||
<div class="hero-card">
|
||||
<div class="hero-avatar" id="hero-avatar">?</div>
|
||||
<div class="hero-main">
|
||||
<div class="hero-name" id="hero-name">{{name}}</div>
|
||||
<div class="hero-meta">
|
||||
<span class="hero-code">{{code}}</span>
|
||||
<span class="hero-stat">{{msg_count}} сообщ.</span>
|
||||
<span class="hero-stat">Визит: {{last_active}}</span>
|
||||
</div>
|
||||
<div class="hero-badges" id="hero-badges"></div>
|
||||
</div>
|
||||
<div class="hero-right">
|
||||
<div class="type-toggle">
|
||||
<button class="type-btn" id="btn-fiz" onclick="setType('fiz')">Физ. лицо</button>
|
||||
<button class="type-btn" id="btn-yur" onclick="setType('yur')">Юр. лицо</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── TAB NAV ── -->
|
||||
<div class="tabs-nav">
|
||||
<button class="tab-btn active" id="tbtn-work" onclick="switchTab('work')">💼 Работа</button>
|
||||
<button class="tab-btn" id="tbtn-interview" onclick="switchTab('interview')">📋 Интервью</button>
|
||||
<button class="tab-btn" id="tbtn-details" onclick="switchTab('details')">🗂 Реквизиты</button>
|
||||
<button class="tab-btn" id="tbtn-history" onclick="switchTab('history')">🕐 История</button>
|
||||
</div>
|
||||
|
||||
<!-- ══ TAB: РАБОТА ══ -->
|
||||
<div class="tab-pane active" id="tab-work">
|
||||
|
||||
<!-- CRM статус -->
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">CRM статус</div>
|
||||
<div class="fields cols3" style="margin-bottom:16px">
|
||||
<div class="field">
|
||||
<label>Статус лида</label>
|
||||
<select id="f-status">
|
||||
<option value="new">🔵 Лид</option>
|
||||
<option value="in_progress">🟡 В работе</option>
|
||||
<option value="proposal">🟠 Предложение отправлено</option>
|
||||
<option value="active">🟢 Работаем</option>
|
||||
<option value="one_time">🟣 Разовая консультация</option>
|
||||
<option value="rejected">🔴 Отказ</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Приоритет</label>
|
||||
<select id="f-priority-override" title="Перебивает автоформулу">
|
||||
<option value="">⚙️ Авто</option>
|
||||
<option value="urgent">🔴 Срочно</option>
|
||||
<option value="high">🟠 Важно</option>
|
||||
<option value="normal">🟡 Обычный</option>
|
||||
<option value="low">⚪ Низкий</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" id="wformat-field" style="display:none">
|
||||
<label>Формат работы</label>
|
||||
<select id="f-work-format">
|
||||
<option value="">— не выбрано —</option>
|
||||
<option value="express">Экспресс-диагностика</option>
|
||||
<option value="audit">Полный аудит + план</option>
|
||||
<option value="turnkey">Внедрение под ключ</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Фаза проекта -->
|
||||
<div class="fields" style="margin-bottom:14px">
|
||||
<div class="field">
|
||||
<label>Фаза проекта</label>
|
||||
<select id="f-project-phase" onchange="updateProcessFlow(this.value)">
|
||||
<option value="">— не начат —</option>
|
||||
<option value="discovery">1 · Диагностика AS-IS</option>
|
||||
<option value="audit">2 · Аудит и отчёт</option>
|
||||
<option value="design">3 · Проектирование TO-BE</option>
|
||||
<option value="impl">4 · Внедрение CRM</option>
|
||||
<option value="support">5 · Сопровождение</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="process-flow-wrap" style="display:none;margin-bottom:14px">
|
||||
<div style="display:flex;gap:0;overflow-x:auto" id="process-flow-steps"></div>
|
||||
<div class="phase-bar" style="margin-top:10px"><div class="phase-bar-fill" id="process-flow-bar" style="width:0"></div></div>
|
||||
<div class="phase-pct-row" style="margin-top:3px"><span>0%</span><span id="process-flow-pct" style="font-weight:700;color:#047857"></span><span>100%</span></div>
|
||||
</div>
|
||||
|
||||
<div class="fields cols1">
|
||||
<div class="field">
|
||||
<label>Потребность / запрос клиента</label>
|
||||
<textarea id="f-need" placeholder="Что хочет клиент, какую задачу решает…" style="min-height:64px"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Задачи -->
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">
|
||||
<span>Задачи</span>
|
||||
<span id="tasks-open-cnt" style="font-size:11px;color:#047857;font-weight:600"></span>
|
||||
</div>
|
||||
<div id="tasks-list"></div>
|
||||
<div class="task-add-row">
|
||||
<input type="text" class="task-input" id="task-input" placeholder="Новая задача…" onkeydown="if(event.key==='Enter')addTask()">
|
||||
<select class="task-prio-sel" id="task-prio">
|
||||
<option value="medium">Обычная</option>
|
||||
<option value="high">Важная</option>
|
||||
<option value="low">Низкая</option>
|
||||
</select>
|
||||
<button class="btn-add" onclick="addTask()">+ Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Примечание -->
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">Примечание консультанта</div>
|
||||
<div class="fields cols1">
|
||||
<div class="field">
|
||||
<textarea id="f-notes" placeholder="Внутренние заметки по клиенту…" style="min-height:80px"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /tab-work -->
|
||||
|
||||
<!-- ══ TAB: ИНТЕРВЬЮ ══ -->
|
||||
<div class="tab-pane" id="tab-interview">
|
||||
|
||||
<!-- Запуск интервью -->
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">
|
||||
<span>ТЗ / Интервью <span id="interview-status-chip"></span></span>
|
||||
<div style="display:flex;gap:8px">
|
||||
<button onclick="openSuggestModal()" style="background:#EFF6FF;border:1.5px solid #BFDBFE;color:#1D4ED8;padding:6px 12px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer">🤖 Вопросы по чату</button>
|
||||
<button onclick="openLaunchModal()" style="background:linear-gradient(135deg,#064E3B,#047857);color:#fff;border:none;padding:6px 14px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:5px">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polygon points="5 3 19 12 5 21 5 3"/></svg> Запустить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="interview-body"><div class="iv-empty">Интервью не запущено</div></div>
|
||||
</div>
|
||||
|
||||
<!-- Первичный бриф -->
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">
|
||||
<span>Первичный бриф</span>
|
||||
<span id="intake-status-chip"></span>
|
||||
</div>
|
||||
<div id="intake-body"><div class="iv-empty">Загрузка…</div></div>
|
||||
</div>
|
||||
|
||||
</div><!-- /tab-interview -->
|
||||
|
||||
<!-- ══ TAB: РЕКВИЗИТЫ ══ -->
|
||||
<div class="tab-pane" id="tab-details">
|
||||
|
||||
<!-- Системные данные -->
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">Системные данные</div>
|
||||
<div class="fields cols3">
|
||||
<div class="field">
|
||||
<label>Имя / Псевдоним</label>
|
||||
<input type="text" id="f-name" placeholder="Отображаемое имя">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>№ Договора</label>
|
||||
<input type="text" id="f-contract" placeholder="КС-2026-001">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Email для входа</label>
|
||||
<input type="email" id="f-email" placeholder="client@company.ru">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Физ. лицо -->
|
||||
<div id="section-fiz">
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">Физическое лицо</div>
|
||||
<div class="fields">
|
||||
<div class="field span2">
|
||||
<label>ФИО полностью</label>
|
||||
<input type="text" id="fiz-full_name" placeholder="Иванов Иван Петрович">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Телефон</label>
|
||||
<input type="tel" id="fiz-phone" placeholder="+7 900 000-00-00">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Дата рождения</label>
|
||||
<input type="text" id="fiz-birthdate" placeholder="01.01.1980">
|
||||
</div>
|
||||
<div class="field span2">
|
||||
<label>Адрес регистрации</label>
|
||||
<input type="text" id="fiz-address" placeholder="г. Москва, ул. Примерная, д. 1, кв. 1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Паспорт — collapsed -->
|
||||
<div class="inner-section">
|
||||
<button class="collapse-toggle" onclick="togglePassport()" id="passport-toggle">
|
||||
<span class="collapse-arrow" id="passport-arrow">▶</span>
|
||||
🔐 Паспортные данные
|
||||
<span style="font-size:11px;color:#CBD5E1;font-weight:400;margin-left:4px">(для договора)</span>
|
||||
</button>
|
||||
<div class="collapse-body" id="passport-body">
|
||||
<div class="fields cols3">
|
||||
<div class="field">
|
||||
<label>Серия</label>
|
||||
<input type="text" id="fiz-passport_series" placeholder="4510">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Номер</label>
|
||||
<input type="text" id="fiz-passport_number" placeholder="123456">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Дата выдачи</label>
|
||||
<input type="text" id="fiz-passport_date" placeholder="15.06.2015">
|
||||
</div>
|
||||
<div class="field span3">
|
||||
<label>Кем выдан</label>
|
||||
<input type="text" id="fiz-passport_issued" placeholder="УФМС России по г. Москве…">
|
||||
</div>
|
||||
<div class="field span3">
|
||||
<label>Код подразделения</label>
|
||||
<input type="text" id="fiz-passport_code" placeholder="770-001">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Юр. лицо -->
|
||||
<div id="section-yur" style="display:none">
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">Юридическое лицо</div>
|
||||
<div class="fields">
|
||||
<div class="field span2">
|
||||
<label>Полное наименование</label>
|
||||
<input type="text" id="yur-org_name" placeholder="ООО «Ромашка»">
|
||||
</div>
|
||||
<div class="field"><label>ИНН</label><input type="text" id="yur-inn" placeholder="7700000000"></div>
|
||||
<div class="field"><label>КПП</label><input type="text" id="yur-kpp" placeholder="770001001"></div>
|
||||
<div class="field"><label>ОГРН</label><input type="text" id="yur-ogrn" placeholder="1027700000000"></div>
|
||||
<div class="field"><label>ОКПО</label><input type="text" id="yur-okpo" placeholder="00000000"></div>
|
||||
<div class="field span2"><label>Юридический адрес</label><input type="text" id="yur-legal_address" placeholder="127000, г. Москва…"></div>
|
||||
<div class="field span2"><label>Фактический адрес</label><input type="text" id="yur-actual_address" placeholder="если отличается"></div>
|
||||
<div class="field"><label>Контактное лицо (ФИО)</label><input type="text" id="yur-contact_person" placeholder="Иванов Иван Иванович"></div>
|
||||
<div class="field"><label>Должность</label><input type="text" id="yur-contact_position" placeholder="Директор"></div>
|
||||
<div class="field"><label>Телефон</label><input type="tel" id="yur-phone" placeholder="+7 495 000-00-00"></div>
|
||||
<div class="field"><label>Действует на основании</label><input type="text" id="yur-basis" placeholder="Устава"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">Банковские реквизиты</div>
|
||||
<div class="fields">
|
||||
<div class="field span2"><label>Банк</label><input type="text" id="yur-bank_name" placeholder="ПАО Сбербанк, г. Москва"></div>
|
||||
<div class="field"><label>Расчётный счёт (р/с)</label><input type="text" id="yur-bank_account" placeholder="40702810000000000000"></div>
|
||||
<div class="field"><label>БИК</label><input type="text" id="yur-bank_bik" placeholder="044525225"></div>
|
||||
<div class="field"><label>Корр. счёт (к/с)</label><input type="text" id="yur-bank_corr" placeholder="30101810400000000225"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /tab-details -->
|
||||
|
||||
<!-- ══ TAB: ИСТОРИЯ ══ -->
|
||||
<div class="tab-pane" id="tab-history">
|
||||
<div class="inner-section">
|
||||
<div class="inner-title">
|
||||
<span>История взаимодействий</span>
|
||||
<span id="events-count" style="font-size:11px;color:#047857;font-weight:600"></span>
|
||||
</div>
|
||||
<div id="events-list"></div>
|
||||
<div class="note-add-row">
|
||||
<input type="text" class="note-input" id="note-input" placeholder="Добавить заметку консультанта…"
|
||||
onkeydown="if(event.key==='Enter')addNote()">
|
||||
<button class="btn-add" onclick="addNote()">+ Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /tab-history -->
|
||||
|
||||
</div><!-- /content-body -->
|
||||
</main>
|
||||
</div><!-- /layout -->
|
||||
|
||||
<!-- ══ MODAL: Launch Interview ══ -->
|
||||
<div class="modal-overlay" id="launch-modal" onclick="if(event.target===this)closeLaunchModal()">
|
||||
<div class="modal-box">
|
||||
<div class="modal-title">🚀 Запустить интервью</div>
|
||||
<div class="modal-field">
|
||||
<label class="modal-label">Фаза проекта</label>
|
||||
<select id="modal-phase" class="modal-select">
|
||||
<option value="discovery">Диагностика AS-IS (10 вопросов)</option>
|
||||
<option value="audit">Аудит (5 вопросов)</option>
|
||||
<option value="design">TO-BE Проектирование (3 вопроса)</option>
|
||||
<option value="impl">Внедрение (3 вопроса)</option>
|
||||
<option value="support">Поддержка (2 вопроса)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label class="modal-label">Формат</label>
|
||||
<div class="modal-radio-row">
|
||||
<label class="modal-radio-lbl" id="mode-qa-lbl">
|
||||
<input type="radio" name="modal-mode" value="qa" checked style="accent-color:#047857">
|
||||
📋 Анкета <span style="font-size:11px;color:#94A3B8">(бесплатно)</span>
|
||||
</label>
|
||||
<label class="modal-radio-lbl" id="mode-dialog-lbl">
|
||||
<input type="radio" name="modal-mode" value="dialog" style="accent-color:#047857">
|
||||
🤖 ИИ-Диалог <span style="font-size:11px;color:#94A3B8">(API)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label class="modal-label">Контекст / первичное обращение <span style="font-weight:400;text-transform:none;color:#CBD5E1">(необязательно)</span></label>
|
||||
<textarea id="modal-context" class="modal-textarea" placeholder="Кратко: откуда клиент, важный контекст для интервью…"></textarea>
|
||||
</div>
|
||||
<div class="modal-tip">Клиент увидит предложение пройти интервью при следующем открытии чата.</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-modal-cancel" onclick="closeLaunchModal()">Отмена</button>
|
||||
<button class="btn-modal-ok" id="launch-btn" onclick="launchInterview()">Запустить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ MODAL: Suggest Questions ══ -->
|
||||
<div class="suggest-overlay" id="suggest-modal" onclick="if(event.target===this)closeSuggestModal()">
|
||||
<div class="suggest-box">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
|
||||
<div style="font-family:'Montserrat',sans-serif;font-size:15px;font-weight:800;color:#0f172a">🤖 AI-вопросы по чату</div>
|
||||
<button onclick="closeSuggestModal()" style="background:#F1F5F9;border:none;width:28px;height:28px;border-radius:7px;cursor:pointer;font-size:15px;color:#64748B">×</button>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label class="modal-label">Фаза</label>
|
||||
<select id="suggest-phase" class="modal-select">
|
||||
<option value="discovery">Диагностика AS-IS</option>
|
||||
<option value="audit">Аудит</option>
|
||||
<option value="design">TO-BE Проектирование</option>
|
||||
<option value="impl">Внедрение</option>
|
||||
<option value="support">Поддержка</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="suggest-result" style="min-height:60px"></div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-modal-cancel" onclick="closeSuggestModal()">Закрыть</button>
|
||||
<button class="btn-modal-blue" id="run-suggest-btn" onclick="runSuggest()">✨ Сформировать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const CODE = '{{code}}';
|
||||
const INIT_TYPE = '{{client_type}}';
|
||||
const CARD = {{card_json}};
|
||||
|
||||
// ── Prefill ──────────────────────────────────────────────────────────────────
|
||||
document.getElementById('f-name').value = '{{name}}';
|
||||
document.getElementById('f-contract').value = '{{contract_no}}';
|
||||
document.getElementById('f-email').value = '{{email}}';
|
||||
document.getElementById('f-notes').value = CARD.notes || '';
|
||||
document.getElementById('f-status').value = '{{status}}' || 'new';
|
||||
document.getElementById('f-need').value = '{{need_text}}';
|
||||
document.getElementById('f-work-format').value = '{{work_format}}';
|
||||
document.getElementById('f-priority-override').value = '{{priority_override}}';
|
||||
|
||||
const _initPhase = CARD.project_phase || '';
|
||||
if(_initPhase){ document.getElementById('f-project-phase').value = _initPhase; updateProcessFlow(_initPhase); }
|
||||
Object.keys(CARD).forEach(k => {
|
||||
const el = document.getElementById('fiz-'+k) || document.getElementById('yur-'+k);
|
||||
if(el) el.value = CARD[k] || '';
|
||||
});
|
||||
|
||||
// ── Hero ─────────────────────────────────────────────────────────────────────
|
||||
function updateHero(){
|
||||
const name = document.getElementById('f-name').value || CODE;
|
||||
document.getElementById('hero-name').textContent = name;
|
||||
document.getElementById('sb-name').textContent = name;
|
||||
const initials = name.split(' ').map(w=>w[0]||'').join('').slice(0,2).toUpperCase() || CODE.slice(0,2);
|
||||
document.getElementById('hero-avatar').textContent = initials;
|
||||
|
||||
const statusLabels = {new:'🔵 Лид',in_progress:'🟡 В работе',proposal:'🟠 Предложение',active:'🟢 Работаем',one_time:'🟣 Разовая',rejected:'🔴 Отказ'};
|
||||
const statusColors = {new:'#EFF6FF:#1D4ED8',in_progress:'#FFFBEB:#D97706',proposal:'#FFF7ED:#EA580C',active:'#ECFDF5:#047857',one_time:'#F5F3FF:#7C3AED',rejected:'#FEF2F2:#DC2626'};
|
||||
const st = document.getElementById('f-status').value;
|
||||
const [sbg,stxt] = (statusColors[st]||'#F1F5F9:#64748B').split(':');
|
||||
const prioMap = {urgent:'🔴 Срочно',high:'🟠 Важно',normal:'🟡 Обычный',low:'⚪ Низкий','':`⚙️ Авто`};
|
||||
const prio = document.getElementById('f-priority-override').value;
|
||||
const phase = document.getElementById('f-project-phase').value;
|
||||
const phaseLabels = {discovery:'AS-IS',audit:'Аудит',design:'TO-BE',impl:'Внедрение',support:'Сопровождение'};
|
||||
|
||||
let html = `<span class="badge" style="background:${sbg};color:${stxt}">${statusLabels[st]||st}</span>`;
|
||||
if(prio) html += `<span class="badge" style="background:#F1F5F9;color:#64748B">${prioMap[prio]}</span>`;
|
||||
if(phase) html += `<span class="badge" style="background:#ECFDF5;color:#047857;border:1px solid #A7F3D0">📍 ${phaseLabels[phase]||phase}</span>`;
|
||||
document.getElementById('hero-badges').innerHTML = html;
|
||||
|
||||
// type chip in breadcrumb
|
||||
const t = currentType;
|
||||
document.getElementById('hdr-type-chip').innerHTML = `<span style="font-size:11px;font-weight:600;padding:2px 9px;border-radius:12px;background:${t==='fiz'?'#F0FDF4':'#EFF6FF'};color:${t==='fiz'?'#059669':'#1D4ED8'};margin-left:8px">${t==='fiz'?'Физ. лицо':'Юр. лицо'}</span>`;
|
||||
}
|
||||
document.getElementById('f-status').addEventListener('change', ()=>{toggleWorkFormat();updateHero();});
|
||||
document.getElementById('f-priority-override').addEventListener('change', updateHero);
|
||||
document.getElementById('f-project-phase').addEventListener('change', updateHero);
|
||||
document.getElementById('f-name').addEventListener('input', updateHero);
|
||||
|
||||
// ── Type ─────────────────────────────────────────────────────────────────────
|
||||
let currentType = INIT_TYPE || 'fiz';
|
||||
function setType(t){
|
||||
currentType = t;
|
||||
document.getElementById('section-fiz').style.display = t==='fiz'?'':'none';
|
||||
document.getElementById('section-yur').style.display = t==='yur'?'':'none';
|
||||
document.getElementById('btn-fiz').classList.toggle('active', t==='fiz');
|
||||
document.getElementById('btn-yur').classList.toggle('active', t==='yur');
|
||||
updateHero();
|
||||
}
|
||||
setType(currentType);
|
||||
|
||||
function toggleWorkFormat(){
|
||||
const s = document.getElementById('f-status').value;
|
||||
document.getElementById('wformat-field').style.display = (s==='active'||s==='proposal')?'':'none';
|
||||
}
|
||||
toggleWorkFormat();
|
||||
|
||||
// ── Tabs ─────────────────────────────────────────────────────────────────────
|
||||
const TAB_IDS = ['work','interview','details','history'];
|
||||
function switchTab(id){
|
||||
TAB_IDS.forEach(t=>{
|
||||
document.getElementById('tab-'+t).classList.toggle('active', t===id);
|
||||
document.getElementById('tbtn-'+t).classList.toggle('active', t===id);
|
||||
const navItem = document.querySelector(`.nav-item[onclick*="'${t}'"]`);
|
||||
if(navItem) navItem.classList.toggle('active', t===id);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Collapse (passport) ──────────────────────────────────────────────────────
|
||||
function togglePassport(){
|
||||
const body = document.getElementById('passport-body');
|
||||
const arrow = document.getElementById('passport-arrow');
|
||||
const open = body.classList.toggle('open');
|
||||
arrow.classList.toggle('open', open);
|
||||
}
|
||||
|
||||
// ── Process flow ──────────────────────────────────────────────────────────────
|
||||
const PHASE_META = {
|
||||
discovery:{label:'Диагностика',short:'AS-IS',bg:'#DBEAFE',color:'#1D4ED8',pct:20},
|
||||
audit: {label:'Аудит', short:'Аудит', bg:'#FEF3C7',color:'#D97706',pct:40},
|
||||
design: {label:'Проектирование',short:'TO-BE',bg:'#F3E8FF',color:'#7C3AED',pct:60},
|
||||
impl: {label:'Внедрение', short:'CRM', bg:'#ECFDF5',color:'#047857',pct:80},
|
||||
support: {label:'Сопровождение',short:'6мес',bg:'#F1F5F9',color:'#64748B',pct:100},
|
||||
};
|
||||
const PHASE_KEYS = ['discovery','audit','design','impl','support'];
|
||||
|
||||
function updateProcessFlow(phase){
|
||||
const wrap = document.getElementById('process-flow-wrap');
|
||||
const bar = document.getElementById('process-flow-bar');
|
||||
const pctEl = document.getElementById('process-flow-pct');
|
||||
const stepsEl= document.getElementById('process-flow-steps');
|
||||
if(!phase){wrap.style.display='none';return;}
|
||||
wrap.style.display='block';
|
||||
const pm = PHASE_META[phase];
|
||||
bar.style.width = pm.pct+'%';
|
||||
pctEl.textContent = pm.pct+'%';
|
||||
const activeIdx = PHASE_KEYS.indexOf(phase);
|
||||
stepsEl.innerHTML = PHASE_KEYS.map((k,i)=>{
|
||||
const m=PHASE_META[k];
|
||||
const isDone=i<activeIdx, isActive=i===activeIdx;
|
||||
const cbg=isDone?'#ECFDF5':isActive?'#047857':'#F9FAFB';
|
||||
const cc =isDone?'#047857':isActive?'#fff':'#9CA3AF';
|
||||
const cb =isDone?'2px solid #10B981':isActive?'none':'2px solid #E5E7EB';
|
||||
const num=isDone?`<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="${cc}" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>`
|
||||
:`<span style="font-size:11px;font-weight:800;color:${cc}">${i+1}</span>`;
|
||||
return `<div style="flex:1;display:flex;flex-direction:column;align-items:center;position:relative;min-width:0">
|
||||
${i>0?`<div style="position:absolute;left:-50%;top:17px;right:50%;height:1.5px;background:${isDone?'#10B981':'#E5E7EB'}"></div>`:''}
|
||||
<div style="width:34px;height:34px;border-radius:50%;background:${cbg};border:${cb};display:flex;align-items:center;justify-content:center;z-index:1;box-shadow:${isActive?'0 4px 12px rgba(4,120,87,.3)':'none'}">${num}</div>
|
||||
<div style="font-size:10px;font-weight:${isActive?700:500};color:${isActive?'#047857':isDone?'#6B7280':'#9CA3AF'};text-align:center;margin-top:5px;line-height:1.2">${m.short}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// ── collectCard ───────────────────────────────────────────────────────────────
|
||||
function collectCard(){
|
||||
const prefix = currentType==='fiz'?'fiz-':'yur-';
|
||||
const card = { notes: document.getElementById('f-notes').value, project_phase: document.getElementById('f-project-phase').value };
|
||||
document.querySelectorAll('[id^="'+prefix+'"]').forEach(el=>{ card[el.id.replace(prefix,'')] = el.value; });
|
||||
return card;
|
||||
}
|
||||
|
||||
// ── Tasks ─────────────────────────────────────────────────────────────────────
|
||||
const PRIO_LABELS = {high:'🔴 Важная',medium:'🟡 Обычная',low:'⚪ Низкая'};
|
||||
async function loadTasks(){
|
||||
const r = await fetch('/consult/api/tasks/'+CODE,{credentials:'include'});
|
||||
if(!r.ok) return;
|
||||
renderTasks(await r.json());
|
||||
}
|
||||
function renderTasks(tasks){
|
||||
const open=tasks.filter(t=>!t.done), done=tasks.filter(t=>t.done);
|
||||
document.getElementById('tasks-open-cnt').textContent = open.length?open.length+' открытых':'';
|
||||
const el=document.getElementById('tasks-list');
|
||||
if(!tasks.length){el.innerHTML='<div style="color:#94A3B8;font-size:13px;padding:4px 0">Задач нет</div>';return;}
|
||||
el.innerHTML=[...open,...done].map(t=>`
|
||||
<div class="task-row">
|
||||
<input type="checkbox" class="task-cb" ${t.done?'checked':''} onchange="toggleTask(${t.id},this.checked)">
|
||||
<span class="task-text ${t.done?'done-text':''}">${t.text}</span>
|
||||
<span class="task-prio-tag" style="background:#F1F5F9;color:#64748B">${PRIO_LABELS[t.priority]||''}</span>
|
||||
<button class="task-del" onclick="deleteTask(${t.id})">×</button>
|
||||
</div>`).join('');
|
||||
}
|
||||
async function addTask(){
|
||||
const inp=document.getElementById('task-input');
|
||||
const text=inp.value.trim(); if(!text)return;
|
||||
inp.value='';
|
||||
await fetch('/consult/api/tasks',{method:'POST',credentials:'include',headers:{'Content-Type':'application/json'},body:JSON.stringify({code:CODE,text,priority:document.getElementById('task-prio').value})});
|
||||
await loadTasks();
|
||||
}
|
||||
async function toggleTask(id,done){
|
||||
await fetch('/consult/api/tasks/'+id+'/done',{method:'PATCH',credentials:'include',headers:{'Content-Type':'application/json'},body:JSON.stringify({done})});
|
||||
await loadTasks();
|
||||
}
|
||||
async function deleteTask(id){
|
||||
if(!confirm('Удалить задачу?'))return;
|
||||
await fetch('/consult/api/tasks/'+id,{method:'DELETE',credentials:'include'});
|
||||
await loadTasks();
|
||||
}
|
||||
loadTasks();
|
||||
|
||||
// ── Interview ─────────────────────────────────────────────────────────────────
|
||||
const PHASE_LABELS={discovery:'Диагностика AS-IS',audit:'Аудит',design:'TO-BE',impl:'Внедрение',support:'Поддержка'};
|
||||
const STATUS_COLORS={pending:{bg:'#FEF3C7',color:'#D97706',text:'Ожидает'},active:{bg:'#DBEAFE',color:'#1D4ED8',text:'Активно'},completed:{bg:'#ECFDF5',color:'#047857',text:'Завершено'}};
|
||||
|
||||
async function loadInterview(){
|
||||
const r=await fetch('/consult/api/admin/interview/'+CODE,{credentials:'include'});
|
||||
if(!r.ok)return;
|
||||
const sessions=await r.json();
|
||||
const body=document.getElementById('interview-body');
|
||||
const chip=document.getElementById('interview-status-chip');
|
||||
if(!sessions.length){body.innerHTML='<div class="iv-empty">Интервью не запущено</div>';chip.innerHTML='';return;}
|
||||
const latest=sessions[0], sc=STATUS_COLORS[latest.status]||STATUS_COLORS.pending;
|
||||
chip.innerHTML=`<span style="font-size:11px;font-weight:600;padding:2px 9px;border-radius:12px;background:${sc.bg};color:${sc.color};margin-left:8px">${sc.text}</span>`;
|
||||
body.innerHTML=sessions.map(s=>renderSessionCard(s)).join('');
|
||||
}
|
||||
function renderSessionCard(s){
|
||||
const sc=STATUS_COLORS[s.status]||STATUS_COLORS.pending;
|
||||
const blocks={};
|
||||
s.questions.forEach(q=>{if(!blocks[q.block])blocks[q.block]=[];blocks[q.block].push(q);});
|
||||
const blocksHtml=Object.entries(blocks).map(([block,qs])=>`
|
||||
<div style="margin-bottom:14px">
|
||||
<div style="font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:#94A3B8;margin-bottom:7px;padding-bottom:3px;border-bottom:1px solid #F1F5F9">${block}</div>
|
||||
${qs.map(q=>`<div style="margin-bottom:9px">
|
||||
<div style="font-size:12px;font-weight:600;color:#475569;margin-bottom:3px">${q.text}</div>
|
||||
${q.answer?`<div style="font-size:13px;color:#0f172a;background:#F8FAFC;border:1px solid #E2E8F0;border-radius:7px;padding:7px 10px;line-height:1.5">${escHtml(q.answer)}</div>`
|
||||
:`<div style="font-size:12px;color:#CBD5E1;font-style:italic">Не отвечено</div>`}
|
||||
</div>`).join('')}
|
||||
</div>`).join('');
|
||||
return `<div class="iv-session">
|
||||
<div class="iv-session-hdr">
|
||||
<div>
|
||||
<span style="font-weight:700;font-size:13px;color:#0f172a">${PHASE_LABELS[s.phase]||s.phase}</span>
|
||||
<span style="font-size:11px;color:#94A3B8;margin-left:8px">${s.mode==='dialog'?'🤖 ИИ-Диалог':'📋 Анкета'}</span>
|
||||
<span style="font-size:11px;color:#94A3B8;margin-left:6px">${s.answered}/${s.total}</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<span style="font-size:11px;font-weight:600;padding:2px 9px;border-radius:12px;background:${sc.bg};color:${sc.color}">${sc.text}</span>
|
||||
<button onclick="deleteInterview(${s.session_id})" style="background:#FEF2F2;border:1px solid #FECACA;color:#DC2626;padding:2px 8px;border-radius:6px;cursor:pointer;font-size:11px">🗑</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:16px">${s.answered>0?blocksHtml:'<div class="iv-empty">Ответов пока нет — ожидаем клиента</div>'}</div>
|
||||
</div>`;
|
||||
}
|
||||
function escHtml(s){return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>');}
|
||||
function openLaunchModal(){document.getElementById('launch-modal').style.display='flex';}
|
||||
function closeLaunchModal(){document.getElementById('launch-modal').style.display='none';}
|
||||
async function launchInterview(){
|
||||
const phase=document.getElementById('modal-phase').value;
|
||||
const mode=document.querySelector('input[name="modal-mode"]:checked').value;
|
||||
const context=document.getElementById('modal-context').value.trim();
|
||||
const btn=document.getElementById('launch-btn');
|
||||
btn.disabled=true;btn.textContent='Запуск…';
|
||||
try{
|
||||
const r=await fetch('/consult/api/admin/interview/start',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({code:CODE,phase,mode,context}),credentials:'include'});
|
||||
const d=await r.json();
|
||||
closeLaunchModal();
|
||||
if(d.ok){document.getElementById('modal-context').value='';await loadInterview();}
|
||||
else if(d.error==='already_active') alert('У клиента уже есть активное интервью для этой фазы.');
|
||||
}catch(e){alert('Ошибка запуска.');}
|
||||
btn.disabled=false;btn.textContent='Запустить';
|
||||
}
|
||||
async function deleteInterview(sessionId){
|
||||
if(!confirm('Удалить интервью и все ответы?'))return;
|
||||
await fetch('/consult/api/admin/interview/'+sessionId,{method:'DELETE',credentials:'include'});
|
||||
await loadInterview();
|
||||
}
|
||||
loadInterview();
|
||||
|
||||
// ── Intake Brief ──────────────────────────────────────────────────────────────
|
||||
const INTAKE_STEPS_META=[
|
||||
{icon:'🏢',title:'Бизнес',fields:['biz_sphere','biz_size','biz_model']},
|
||||
{icon:'🔥',title:'Боль',fields:['pain_main','pain_cost','pain_tried']},
|
||||
{icon:'⚙️',title:'Процесс',fields:['proc_steps','proc_bottleneck','proc_tools']},
|
||||
{icon:'🎯',title:'Цель',fields:['goal_result','goal_metrics','goal_budget']},
|
||||
{icon:'📍',title:'Контекст',fields:['ctx_team','ctx_deadline','ctx_decider']},
|
||||
];
|
||||
const INTAKE_LABELS={
|
||||
biz_sphere:'Сфера',biz_size:'Размер',biz_model:'Бизнес-модель',
|
||||
pain_main:'Проблема',pain_cost:'Цена боли',pain_tried:'Пробовали',
|
||||
proc_steps:'Шаги',proc_bottleneck:'Узкое место',proc_tools:'Инструменты',
|
||||
goal_result:'Результат',goal_metrics:'Метрики',goal_budget:'Бюджет',
|
||||
ctx_team:'Команда',ctx_deadline:'Сроки',ctx_decider:'ЛПР',
|
||||
};
|
||||
async function loadIntakeBrief(){
|
||||
try{
|
||||
const r=await fetch('/consult/api/admin/intake/'+CODE,{credentials:'include'});
|
||||
if(!r.ok)return;
|
||||
const data=await r.json();
|
||||
const body=document.getElementById('intake-body');
|
||||
const chip=document.getElementById('intake-status-chip');
|
||||
const allKeys=Object.keys(INTAKE_LABELS);
|
||||
const filled=allKeys.filter(k=>data[k]&&data[k].value&&data[k].value.trim()).length;
|
||||
if(!filled){body.innerHTML='<div class="iv-empty">Клиент ещё не заполнил бриф</div>';chip.innerHTML='';return;}
|
||||
const pct=Math.round(filled/allKeys.length*100);
|
||||
const isComplete=filled===allKeys.length;
|
||||
chip.innerHTML=`<span style="font-size:11px;font-weight:600;padding:2px 9px;border-radius:12px;background:${isComplete?'#ECFDF5':'#FEF3C7'};color:${isComplete?'#047857':'#D97706'}">${isComplete?'✅ Заполнен':pct+'% заполнено'}</span>`;
|
||||
body.innerHTML=`
|
||||
<div style="height:4px;background:#F1F5F9;border-radius:4px;overflow:hidden;margin-bottom:16px">
|
||||
<div style="height:100%;width:${pct}%;background:linear-gradient(90deg,#047857,#10B981);border-radius:4px"></div>
|
||||
</div>
|
||||
<div class="intake-grid">
|
||||
${INTAKE_STEPS_META.map(step=>{
|
||||
const hasAny=step.fields.some(k=>data[k]&&data[k].value&&data[k].value.trim());
|
||||
if(!hasAny) return `<div class="intake-block empty"><div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#94A3B8;margin-bottom:6px">${step.icon} ${step.title}</div><div style="font-size:12px;color:#CBD5E1;font-style:italic">Не заполнено</div></div>`;
|
||||
return `<div class="intake-block"><div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#047857;margin-bottom:10px">${step.icon} ${step.title}</div>
|
||||
${step.fields.map(k=>{
|
||||
const v=data[k];
|
||||
if(!v||!v.value||!v.value.trim()) return `<div style="margin-bottom:5px"><span style="font-size:11px;color:#CBD5E1">${INTAKE_LABELS[k]}: <i>не указано</i></span></div>`;
|
||||
return `<div style="margin-bottom:9px"><div class="intake-field-label">${INTAKE_LABELS[k]}</div><div class="intake-field-val">${escHtml(v.value)}</div></div>`;
|
||||
}).join('')}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>`;
|
||||
}catch(e){}
|
||||
}
|
||||
loadIntakeBrief();
|
||||
|
||||
// ── Events ────────────────────────────────────────────────────────────────────
|
||||
async function loadEvents(){
|
||||
try{
|
||||
const r=await fetch('/consult/api/admin/events/'+CODE,{credentials:'include'});
|
||||
if(!r.ok)return;
|
||||
const events=await r.json();
|
||||
document.getElementById('events-count').textContent=events.length?events.length+' событий':'';
|
||||
const list=document.getElementById('events-list');
|
||||
if(!events.length){list.innerHTML='<div style="color:#94A3B8;font-size:13px;padding:4px 0">Активности пока нет</div>';return;}
|
||||
list.innerHTML=events.map(e=>`
|
||||
<div class="event-row">
|
||||
<div class="event-icon">${evIcon(e.event_type)}</div>
|
||||
<div><div class="event-title">${escHtml(e.title)}</div><div class="event-ts">${fmtTime(e.created_at)}</div></div>
|
||||
</div>`).join('');
|
||||
}catch(e){}
|
||||
}
|
||||
function evIcon(t){
|
||||
if(t.startsWith('intake')) return t==='intake_completed'?'✅':'📝';
|
||||
if(t.startsWith('interview')) return '📋';
|
||||
if(t==='admin_note') return '📌';
|
||||
if(t==='chat_message') return '💬';
|
||||
if(t==='contract_viewed') return '📄';
|
||||
return '📎';
|
||||
}
|
||||
function fmtTime(ts){
|
||||
if(!ts)return'';
|
||||
try{const d=new Date(ts.replace(' ','T'));return d.toLocaleDateString('ru',{day:'2-digit',month:'2-digit',year:'numeric'})+' '+d.toLocaleTimeString('ru',{hour:'2-digit',minute:'2-digit'});}catch(e){return ts;}
|
||||
}
|
||||
async function addNote(){
|
||||
const inp=document.getElementById('note-input');
|
||||
const note=inp.value.trim();if(!note)return;
|
||||
inp.value='';
|
||||
await fetch('/consult/api/admin/events/'+CODE+'/note',{method:'POST',credentials:'include',headers:{'Content-Type':'application/json'},body:JSON.stringify({note})});
|
||||
await loadEvents();
|
||||
}
|
||||
loadEvents();
|
||||
|
||||
// ── Suggest Questions ─────────────────────────────────────────────────────────
|
||||
function openSuggestModal(){
|
||||
document.getElementById('suggest-result').innerHTML='<div style="font-size:13px;color:#94A3B8;text-align:center;padding:20px 0">Выберите фазу и нажмите «Сформировать»</div>';
|
||||
document.getElementById('suggest-modal').style.display='flex';
|
||||
}
|
||||
function closeSuggestModal(){document.getElementById('suggest-modal').style.display='none';}
|
||||
async function runSuggest(){
|
||||
const phase=document.getElementById('suggest-phase').value;
|
||||
const btn=document.getElementById('run-suggest-btn');
|
||||
const res=document.getElementById('suggest-result');
|
||||
btn.disabled=true;btn.textContent='⏳ Анализирую…';
|
||||
res.innerHTML='<div style="font-size:13px;color:#64748B;padding:16px 0;text-align:center">AI читает переписку…</div>';
|
||||
try{
|
||||
const r=await fetch('/consult/api/admin/interview/suggest-questions',{method:'POST',credentials:'include',headers:{'Content-Type':'application/json'},body:JSON.stringify({code:CODE,phase})});
|
||||
const d=await r.json();
|
||||
if(d.ok&&d.questions&&d.questions.length){
|
||||
res.innerHTML=`<div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:#94A3B8;margin-bottom:10px">${d.questions.length} персонализированных вопросов</div>
|
||||
${d.questions.map((q,i)=>`<div style="border:1.5px solid #E5E7EB;border-radius:10px;padding:11px 13px;margin-bottom:7px;background:#FAFAFA">
|
||||
<div style="font-size:13px;font-weight:600;color:#0f172a;margin-bottom:3px">${i+1}. ${escHtml(q.text)}</div>
|
||||
${q.why?`<div style="font-size:11px;color:#94A3B8">💡 ${escHtml(q.why)}</div>`:''}
|
||||
</div>`).join('')}
|
||||
<div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:8px;padding:9px 12px;margin-top:8px;font-size:12px;color:#1D4ED8">Скопируйте нужные вопросы для контекста интервью.</div>`;
|
||||
} else if(d.error==='no_data'){
|
||||
res.innerHTML='<div style="background:#FEF3C7;border:1px solid #FDE68A;border-radius:8px;padding:11px;font-size:13px;color:#92400E">У клиента ещё нет переписки. Попросите клиента написать первые сообщения.</div>';
|
||||
} else {
|
||||
res.innerHTML=`<div style="background:#FEF2F2;border:1px solid #FECACA;border-radius:8px;padding:11px;font-size:13px;color:#991B1B">Ошибка: ${escHtml(d.error||'неизвестная')}</div>`;
|
||||
}
|
||||
}catch(e){res.innerHTML='<div style="background:#FEF2F2;border:1px solid #FECACA;border-radius:8px;padding:11px;font-size:13px;color:#991B1B">Ошибка подключения.</div>';}
|
||||
btn.disabled=false;btn.textContent='✨ Сформировать';
|
||||
}
|
||||
|
||||
// ── Save ──────────────────────────────────────────────────────────────────────
|
||||
async function saveCard(){
|
||||
const status=document.getElementById('save-status');
|
||||
status.textContent='Сохраняю…';status.className='save-status';
|
||||
const payload={
|
||||
client_type:currentType,
|
||||
name:document.getElementById('f-name').value,
|
||||
contract_no:document.getElementById('f-contract').value,
|
||||
email:document.getElementById('f-email').value,
|
||||
status:document.getElementById('f-status').value,
|
||||
need_text:document.getElementById('f-need').value,
|
||||
work_format:document.getElementById('f-work-format').value,
|
||||
priority_override:document.getElementById('f-priority-override').value,
|
||||
card:collectCard()
|
||||
};
|
||||
try{
|
||||
const res=await fetch('/consult/api/admin/update-client/'+CODE,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload),credentials:'include'});
|
||||
if(!res.ok) throw new Error(res.status);
|
||||
status.textContent='✓ Сохранено';status.className='save-status ok';
|
||||
updateHero();
|
||||
setTimeout(()=>{status.textContent='';},3000);
|
||||
}catch(e){status.textContent='✗ Ошибка';status.className='save-status err';}
|
||||
}
|
||||
|
||||
// ── Init hero ─────────────────────────────────────────────────────────────────
|
||||
updateHero();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
388
Mokap/consult_landing.html
Normal file
388
Mokap/consult_landing.html
Normal file
@ -0,0 +1,388 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI КОНСАЛТИНГ CRM — @wasrusgen1</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg: #0F172A;
|
||||
--bg2: #1E293B;
|
||||
--bg3: #0D1B2A;
|
||||
--accent: #10B981;
|
||||
--accent2: #047857;
|
||||
--accent3: #34D399;
|
||||
--text: #F1F5F9;
|
||||
--muted: #94A3B8;
|
||||
--border: #334155;
|
||||
}
|
||||
html { scroll-behavior: smooth; }
|
||||
body { background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; font-size: 16px; line-height: 1.6; -webkit-font-smoothing: antialiased; }
|
||||
h1,h2,h3 { font-family: 'Montserrat', sans-serif; }
|
||||
a { text-decoration: none; color: inherit; }
|
||||
.container { max-width: 1120px; margin: 0 auto; padding: 0 24px; }
|
||||
|
||||
/* NAV */
|
||||
nav {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
background: rgba(15,23,42,0.85); backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 0 24px;
|
||||
height: 64px; display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.nav-logo { font-family: 'Montserrat', sans-serif; font-weight: 800; font-size: 20px; color: var(--accent); letter-spacing: -0.5px; }
|
||||
.nav-logo span { color: var(--text); }
|
||||
.nav-links { display: flex; gap: 32px; }
|
||||
.nav-links a { font-size: 14px; font-weight: 500; color: var(--muted); transition: color 0.2s; }
|
||||
.nav-links a:hover { color: var(--text); }
|
||||
.nav-cta { background: var(--accent); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 14px; font-weight: 600; transition: background 0.2s; }
|
||||
.nav-cta:hover { background: var(--accent2); color: #fff; }
|
||||
|
||||
/* HERO */
|
||||
.hero {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #064E3B 0%, #0F172A 50%, #0F172A 100%);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
text-align: center; padding: 80px 24px 60px;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.hero::before {
|
||||
content: '';
|
||||
position: absolute; inset: 0;
|
||||
background: radial-gradient(ellipse 80% 60% at 50% 30%, rgba(16,185,129,0.12) 0%, transparent 70%);
|
||||
}
|
||||
.hero-content { position: relative; z-index: 1; max-width: 800px; }
|
||||
.hero-badge {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
background: rgba(16,185,129,0.12); border: 1px solid rgba(16,185,129,0.3);
|
||||
color: var(--accent3); padding: 6px 16px; border-radius: 100px;
|
||||
font-size: 13px; font-weight: 600; letter-spacing: 0.5px; margin-bottom: 28px;
|
||||
}
|
||||
.hero-badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: block; }
|
||||
.hero h1 { font-size: clamp(32px, 6vw, 60px); font-weight: 800; line-height: 1.1; margin-bottom: 20px; letter-spacing: -1px; }
|
||||
.hero h1 em { font-style: normal; color: var(--accent); }
|
||||
.hero p { font-size: clamp(16px, 2vw, 20px); color: var(--muted); max-width: 600px; margin: 0 auto 40px; }
|
||||
.hero-btns { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }
|
||||
.btn-primary {
|
||||
background: var(--accent); color: #fff; padding: 14px 32px; border-radius: 10px;
|
||||
font-size: 16px; font-weight: 600; transition: all 0.2s;
|
||||
box-shadow: 0 4px 20px rgba(16,185,129,0.3);
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent2); transform: translateY(-1px); box-shadow: 0 8px 30px rgba(16,185,129,0.4); }
|
||||
.btn-outline {
|
||||
background: transparent; color: var(--text); padding: 14px 32px; border-radius: 10px;
|
||||
font-size: 16px; font-weight: 600; border: 1px solid var(--border); transition: all 0.2s;
|
||||
}
|
||||
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.hero-stat { display: flex; gap: 48px; justify-content: center; margin-top: 60px; padding-top: 40px; border-top: 1px solid var(--border); }
|
||||
.stat-item { text-align: center; }
|
||||
.stat-num { font-family: 'Montserrat', sans-serif; font-size: 28px; font-weight: 800; color: var(--accent); }
|
||||
.stat-label { font-size: 13px; color: var(--muted); margin-top: 4px; }
|
||||
|
||||
/* SECTIONS */
|
||||
section { padding: 96px 24px; }
|
||||
.section-tag { font-size: 13px; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: 1.5px; margin-bottom: 12px; }
|
||||
.section-title { font-size: clamp(26px, 4vw, 40px); font-weight: 800; margin-bottom: 16px; letter-spacing: -0.5px; }
|
||||
.section-sub { font-size: 17px; color: var(--muted); max-width: 560px; }
|
||||
|
||||
/* PROBLEM */
|
||||
.problem { background: var(--bg); }
|
||||
.problem-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; margin-top: 56px; }
|
||||
.problem-card {
|
||||
background: var(--bg2); border: 1px solid var(--border); border-radius: 16px;
|
||||
padding: 32px; position: relative; overflow: hidden;
|
||||
}
|
||||
.problem-card::before {
|
||||
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
|
||||
background: linear-gradient(90deg, #EF4444, #F97316);
|
||||
}
|
||||
.problem-icon { font-size: 32px; margin-bottom: 16px; }
|
||||
.problem-card h3 { font-size: 18px; font-weight: 700; margin-bottom: 10px; font-family: 'Inter', sans-serif; }
|
||||
.problem-card p { font-size: 14px; color: var(--muted); }
|
||||
|
||||
/* FEATURES */
|
||||
.features { background: linear-gradient(180deg, #0A1628 0%, var(--bg) 100%); }
|
||||
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-top: 56px; }
|
||||
.feature-card {
|
||||
background: var(--bg2); border: 1px solid var(--border); border-radius: 16px;
|
||||
padding: 28px; transition: border-color 0.2s, transform 0.2s;
|
||||
}
|
||||
.feature-card:hover { border-color: rgba(16,185,129,0.4); transform: translateY(-2px); }
|
||||
.feature-icon { width: 48px; height: 48px; background: rgba(16,185,129,0.1); border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 22px; margin-bottom: 16px; }
|
||||
.feature-card h3 { font-size: 17px; font-weight: 700; margin-bottom: 8px; font-family: 'Inter', sans-serif; }
|
||||
.feature-card p { font-size: 14px; color: var(--muted); }
|
||||
|
||||
/* FUNNEL */
|
||||
.funnel { background: var(--bg2); }
|
||||
.funnel-flow { display: flex; align-items: flex-start; gap: 0; margin-top: 56px; overflow-x: auto; padding-bottom: 16px; }
|
||||
.funnel-step {
|
||||
flex: 1; min-width: 160px;
|
||||
background: var(--bg); border: 1px solid var(--border); border-radius: 14px;
|
||||
padding: 24px 20px; text-align: center; position: relative;
|
||||
}
|
||||
.funnel-step.active { border-color: var(--accent); background: rgba(16,185,129,0.05); }
|
||||
.funnel-arrow {
|
||||
flex: 0 0 32px; display: flex; align-items: center; justify-content: center;
|
||||
color: var(--accent); font-size: 20px; padding-top: 36px;
|
||||
}
|
||||
.funnel-num {
|
||||
width: 32px; height: 32px; border-radius: 50%; background: rgba(16,185,129,0.15);
|
||||
color: var(--accent); font-weight: 700; font-size: 14px;
|
||||
display: flex; align-items: center; justify-content: center; margin: 0 auto 12px;
|
||||
}
|
||||
.funnel-step.active .funnel-num { background: var(--accent); color: #fff; }
|
||||
.funnel-step h4 { font-size: 14px; font-weight: 700; margin-bottom: 8px; }
|
||||
.funnel-step p { font-size: 12px; color: var(--muted); line-height: 1.5; }
|
||||
.funnel-desc { margin-top: 40px; display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
|
||||
|
||||
/* DEMO */
|
||||
.demo { background: var(--bg); }
|
||||
.demo-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; margin-top: 56px; }
|
||||
.demo-card {
|
||||
background: var(--bg2); border: 1px solid var(--border); border-radius: 16px;
|
||||
padding: 28px; transition: all 0.2s;
|
||||
}
|
||||
.demo-card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0,0,0,0.2); }
|
||||
.demo-num { font-family: 'Montserrat', sans-serif; font-size: 36px; font-weight: 800; color: rgba(16,185,129,0.2); margin-bottom: 16px; line-height: 1; }
|
||||
.demo-card h3 { font-size: 17px; font-weight: 700; margin-bottom: 8px; }
|
||||
.demo-card p { font-size: 13px; color: var(--muted); margin-bottom: 20px; }
|
||||
.demo-link {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
color: var(--accent); font-size: 14px; font-weight: 600;
|
||||
transition: gap 0.2s;
|
||||
}
|
||||
.demo-link:hover { gap: 10px; }
|
||||
.demo-link::after { content: '→'; }
|
||||
|
||||
/* FOOTER */
|
||||
footer {
|
||||
background: var(--bg3); border-top: 1px solid var(--border);
|
||||
padding: 48px 24px;
|
||||
}
|
||||
.footer-inner { max-width: 1120px; margin: 0 auto; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 24px; }
|
||||
.footer-brand .logo { font-family: 'Montserrat', sans-serif; font-weight: 800; font-size: 20px; color: var(--accent); }
|
||||
.footer-brand p { font-size: 13px; color: var(--muted); margin-top: 6px; }
|
||||
.footer-links { display: flex; gap: 24px; }
|
||||
.footer-links a { font-size: 14px; color: var(--muted); transition: color 0.2s; }
|
||||
.footer-links a:hover { color: var(--text); }
|
||||
.footer-contact { font-size: 14px; color: var(--muted); }
|
||||
.footer-contact a { color: var(--accent); }
|
||||
.footer-bottom { max-width: 1120px; margin: 24px auto 0; padding-top: 24px; border-top: 1px solid var(--border); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; }
|
||||
.footer-bottom p { font-size: 13px; color: var(--muted); }
|
||||
|
||||
@media(max-width: 768px) {
|
||||
.nav-links { display: none; }
|
||||
.hero-stat { gap: 24px; flex-wrap: wrap; }
|
||||
.funnel-flow { flex-wrap: nowrap; }
|
||||
.footer-inner { justify-content: center; text-align: center; }
|
||||
.footer-links { justify-content: center; }
|
||||
.footer-bottom { justify-content: center; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- NAV -->
|
||||
<nav>
|
||||
<div class="nav-logo">@wasrusgen<span>1</span></div>
|
||||
<div class="nav-links">
|
||||
<a href="#problem">Проблема</a>
|
||||
<a href="#features">Возможности</a>
|
||||
<a href="#funnel">Воронка</a>
|
||||
<a href="#demo">Демо</a>
|
||||
</div>
|
||||
<a href="https://wasrusgen1.ru/consult/" target="_blank" class="nav-cta">Открыть систему</a>
|
||||
</nav>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="hero">
|
||||
<div class="hero-content">
|
||||
<div class="hero-badge">AI КОНСАЛТИНГ ПЛАТФОРМА</div>
|
||||
<h1>AI-Ассистент для<br><em>консалтингового</em> бизнеса</h1>
|
||||
<p>Структурирует сбор информации о клиенте. Ведёт через все этапы проекта. Готовит документы.</p>
|
||||
<div class="hero-btns">
|
||||
<a href="#demo" class="btn-primary">Посмотреть демо</a>
|
||||
<a href="https://wasrusgen1.ru/consult/" target="_blank" class="btn-outline">Открыть систему</a>
|
||||
</div>
|
||||
<div class="hero-stat">
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">5</div>
|
||||
<div class="stat-label">фаз проекта</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">AI</div>
|
||||
<div class="stat-label">ассистент Елена</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">0</div>
|
||||
<div class="stat-label">ручных документов</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- PROBLEM -->
|
||||
<section class="problem" id="problem">
|
||||
<div class="container">
|
||||
<div class="section-tag">Проблема</div>
|
||||
<h2 class="section-title">Почему консалтинг теряет деньги</h2>
|
||||
<p class="section-sub">Три системных проблемы, которые съедают время и снижают качество работы с клиентами</p>
|
||||
<div class="problem-grid">
|
||||
<div class="problem-card">
|
||||
<div class="problem-icon">🗣️</div>
|
||||
<h3>Клиент рассказывает обо всём, кроме сути</h3>
|
||||
<p>Первичная встреча превращается в монолог без структуры. Консультант получает 80% нерелевантной информации и 20% нужной.</p>
|
||||
</div>
|
||||
<div class="problem-card">
|
||||
<div class="problem-icon">📋</div>
|
||||
<h3>Нет структуры сбора информации</h3>
|
||||
<p>Каждый проект начинается с нуля. Нет единого шаблона брифа, нет контрольного списка вопросов, нет истории диалога.</p>
|
||||
</div>
|
||||
<div class="problem-card">
|
||||
<div class="problem-icon">📝</div>
|
||||
<h3>Документы готовятся вручную</h3>
|
||||
<p>ТЗ, договоры, отчёты — каждый раз копипаст из предыдущих проектов. Занимает часы, в которые можно работать с клиентами.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<section class="features" id="features">
|
||||
<div class="container">
|
||||
<div class="section-tag">Решение</div>
|
||||
<h2 class="section-title">Возможности платформы</h2>
|
||||
<p class="section-sub">Всё что нужно для системной работы с клиентами — в одном месте</p>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🎙️</div>
|
||||
<h3>Первичный бриф с голосом</h3>
|
||||
<p>5 структурированных блоков с поддержкой голосового ввода. Клиент говорит — система записывает и структурирует.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🤖</div>
|
||||
<h3>AI-ассистент с ограничениями</h3>
|
||||
<p>Ассистент Елена ведёт диалог строго в рамках задачи. Не уходит в сторону, фокусирует клиента на главном.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📊</div>
|
||||
<h3>Структурированное интервью</h3>
|
||||
<p>Система вопросов с прогрессом прохождения. Все ответы сохраняются, можно продолжить в любой момент.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📄</div>
|
||||
<h3>Генерация ТЗ и договоров</h3>
|
||||
<p>Документы автоматически формируются из данных карточки клиента. Редактирование и экспорт одним кликом.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🗂️</div>
|
||||
<h3>CRM с воронкой проекта</h3>
|
||||
<p>Каждый клиент проходит 5 фаз: от первичного анализа до сопровождения. Статус всегда виден в кабинете.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FUNNEL -->
|
||||
<section class="funnel" id="funnel">
|
||||
<div class="container">
|
||||
<div class="section-tag">Воронка проекта</div>
|
||||
<h2 class="section-title">5 фаз работы с клиентом</h2>
|
||||
<p class="section-sub">Стандартизированный процесс от первого контакта до долгосрочного сопровождения</p>
|
||||
<div class="funnel-flow">
|
||||
<div class="funnel-step">
|
||||
<div class="funnel-num">1</div>
|
||||
<h4>AS-IS</h4>
|
||||
<p>Анализ текущего состояния бизнеса клиента</p>
|
||||
</div>
|
||||
<div class="funnel-arrow">›</div>
|
||||
<div class="funnel-step">
|
||||
<div class="funnel-num">2</div>
|
||||
<h4>Аудит</h4>
|
||||
<p>Детальный разбор процессов, выявление узких мест</p>
|
||||
</div>
|
||||
<div class="funnel-arrow">›</div>
|
||||
<div class="funnel-step active">
|
||||
<div class="funnel-num">3</div>
|
||||
<h4>TO-BE</h4>
|
||||
<p>Проектирование целевого состояния</p>
|
||||
</div>
|
||||
<div class="funnel-arrow">›</div>
|
||||
<div class="funnel-step">
|
||||
<div class="funnel-num">4</div>
|
||||
<h4>Внедрение</h4>
|
||||
<p>Реализация изменений, обучение команды</p>
|
||||
</div>
|
||||
<div class="funnel-arrow">›</div>
|
||||
<div class="funnel-step">
|
||||
<div class="funnel-num">5</div>
|
||||
<h4>Сопровождение</h4>
|
||||
<p>Поддержка, контроль результатов, доработки</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- DEMO -->
|
||||
<section class="demo" id="demo">
|
||||
<div class="container">
|
||||
<div class="section-tag">Демо</div>
|
||||
<h2 class="section-title">Посмотрите систему в работе</h2>
|
||||
<p class="section-sub">Четыре ключевых модуля — доступны прямо сейчас</p>
|
||||
<div class="demo-grid">
|
||||
<div class="demo-card">
|
||||
<div class="demo-num">01</div>
|
||||
<h3>Кабинет клиента</h3>
|
||||
<p>Workspace с чатом AI-ассистента Елены, брифом и интервью</p>
|
||||
<a href="https://wasrusgen1.ru/consult/" target="_blank" class="demo-link">Открыть</a>
|
||||
</div>
|
||||
<div class="demo-card">
|
||||
<div class="demo-num">02</div>
|
||||
<h3>Кабинет администратора</h3>
|
||||
<p>Список клиентов с приоритетами, статусами и управлением</p>
|
||||
<a href="https://wasrusgen1.ru/consult/admin" target="_blank" class="demo-link">Открыть</a>
|
||||
</div>
|
||||
<div class="demo-card">
|
||||
<div class="demo-num">03</div>
|
||||
<h3>Карточка клиента</h3>
|
||||
<p>Воронка проекта, бриф, ТЗ, история взаимодействий</p>
|
||||
<a href="https://wasrusgen1.ru/consult/client" target="_blank" class="demo-link">Открыть</a>
|
||||
</div>
|
||||
<div class="demo-card">
|
||||
<div class="demo-num">04</div>
|
||||
<h3>Договор</h3>
|
||||
<p>Автогенерация договора из данных карточки клиента</p>
|
||||
<a href="https://wasrusgen1.ru/consult/contract" target="_blank" class="demo-link">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer>
|
||||
<div class="footer-inner">
|
||||
<div class="footer-brand">
|
||||
<div class="logo">@wasrusgen1</div>
|
||||
<p>AI-консалтинг для производственного бизнеса</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a href="#problem">Проблема</a>
|
||||
<a href="#features">Возможности</a>
|
||||
<a href="#funnel">Воронка</a>
|
||||
<a href="#demo">Демо</a>
|
||||
</div>
|
||||
<div class="footer-contact">
|
||||
ИП Васильев Р.Г. | <a href="https://wasrusgen1.ru/consult/" target="_blank">wasrusgen1.ru</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>© 2025 @wasrusgen1 — AI КОНСАЛТИНГ CRM</p>
|
||||
<p>Все права защищены</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
64
Mokap/index.html
Normal file
64
Mokap/index.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@wasrusgen1 CRM — Мокапы</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:'Inter',sans-serif;background:#F5F6F8;min-height:100vh;padding:40px 20px}
|
||||
h1{font-size:22px;font-weight:800;color:#1A1A2E;margin-bottom:4px}
|
||||
.sub{font-size:13px;color:#8A94A6;margin-bottom:32px}
|
||||
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px;max-width:900px;margin:0 auto}
|
||||
.card{background:#fff;border-radius:16px;padding:24px;box-shadow:0 1px 4px rgba(0,0,0,.08);transition:box-shadow .15s,transform .15s;text-decoration:none;display:block}
|
||||
.card:hover{box-shadow:0 4px 16px rgba(0,0,0,.12);transform:translateY(-2px)}
|
||||
.role{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#8A94A6;margin-bottom:8px}
|
||||
.title{font-size:17px;font-weight:800;color:#1A1A2E;margin-bottom:6px}
|
||||
.desc{font-size:12px;color:#8A94A6;line-height:1.5}
|
||||
.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:600;margin-top:14px}
|
||||
.done{background:#F0FDF4;color:#16A34A}
|
||||
.wip{background:#FFFBEB;color:#D97706}
|
||||
.soon{background:#F1F5F9;color:#8A94A6}
|
||||
.header{max-width:900px;margin:0 auto 28px}
|
||||
.logo{font-size:13px;font-weight:700;color:#003E7E;margin-bottom:16px;display:flex;align-items:center;gap:8px}
|
||||
.logo span{color:#76BD22}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="logo">@wasrusgen1 <span>CRM</span></div>
|
||||
<h1>Мокапы кабинетов</h1>
|
||||
<div class="sub">Telegram MiniApp · Мебельный дилер · ИП Васильев Р.Г.</div>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<a class="card" href="mockup_owner.html">
|
||||
<div class="role">Роль 1</div>
|
||||
<div class="title">Генеральный директор</div>
|
||||
<div class="desc">Выручка по салонам, финансы, команда агрегированно, склад (статус поставок)</div>
|
||||
<div class="badge done">✓ Принято как база</div>
|
||||
</a>
|
||||
|
||||
<a class="card" href="mockup_commercial.html">
|
||||
<div class="role">Роль 2</div>
|
||||
<div class="title">Коммерческий директор</div>
|
||||
<div class="desc">Воронка продаж, заказы, персонал (администраторы), закупки, финансы план/факт</div>
|
||||
<div class="badge wip">⚙ В разработке</div>
|
||||
</a>
|
||||
|
||||
<div class="card" style="opacity:.55;cursor:default">
|
||||
<div class="role">Роль 3</div>
|
||||
<div class="title">Директор по сервису</div>
|
||||
<div class="desc">Доставки, монтажники, замерщики, рекламации, NPS</div>
|
||||
<div class="badge soon">Скоро</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="opacity:.55;cursor:default">
|
||||
<div class="role">Роль 4</div>
|
||||
<div class="title">Администратор салона</div>
|
||||
<div class="desc">Оперативное управление салоном: заказы, менеджеры, закупки, смена</div>
|
||||
<div class="badge soon">Скоро</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
Mokap/logo_w1_blue.png
Normal file
BIN
Mokap/logo_w1_blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
Mokap/logo_w1_white.png
Normal file
BIN
Mokap/logo_w1_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
1761
Mokap/mockup_admin.html
Normal file
1761
Mokap/mockup_admin.html
Normal file
File diff suppressed because it is too large
Load Diff
2196
Mokap/mockup_assembler.html
Normal file
2196
Mokap/mockup_assembler.html
Normal file
File diff suppressed because it is too large
Load Diff
853
Mokap/mockup_commercial.html
Normal file
853
Mokap/mockup_commercial.html
Normal file
@ -0,0 +1,853 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@wasrusgen1 CRM — Коммерческий директор</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Montserrat:wght@700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{background:#C8CACD;min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px;font-family:'Inter',sans-serif}
|
||||
body{--accent:#003E7E;--accent2:#76BD22;--bg:#F5F6F8;--card:#FFFFFF;--ink:#1A1A2E;--muted:#8A94A6;--danger:#EF4444;--warn:#F59E0B;--success:#10B981;--s-success-bg:#ECFDF5;--s-warning-bg:#FFFBEB;--s-danger-bg:#FEF2F2;--s-info:#3B82F6;--s-info-bg:#EFF6FF}
|
||||
body[data-theme="radar"]{--accent:#4338CA;--accent2:#6366F1;--bg:#F9FAFB;--card:#FFFFFF;--ink:#111827;--muted:#6B7280}
|
||||
body[data-theme="dark"]{--accent:#4338CA;--accent2:#6366F1;--bg:#111827;--card:#1F2937;--ink:#F9FAFB;--muted:#9CA3AF}
|
||||
|
||||
#controls{display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap;justify-content:center;width:100%;max-width:600px}
|
||||
#controls label{color:#fff;font-size:13px;font-weight:600}
|
||||
#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:linear-gradient(135deg,#1E1B4B,#4338CA);color:#fff}
|
||||
.theme-btn[data-t="dark"]{background:#111827;color:#6366F1}
|
||||
|
||||
#phoneFrame{width:390px;height:844px;background:var(--bg);border-radius:44px;overflow:hidden;box-shadow:0 24px 80px rgba(0,0,0,.4),inset 0 0 0 1px rgba(255,255,255,.15);position:relative;display:flex;flex-direction:column}
|
||||
#statusBar{height:44px;background:var(--card);display:flex;align-items:center;justify-content:space-between;padding:0 24px;flex-shrink:0;font-size:13px;font-weight:600;color:var(--ink);z-index:10}
|
||||
.sb-r{display:flex;align-items:center;gap:6px}
|
||||
#screen{flex:1;overflow-y:auto;overflow-x:hidden;scrollbar-width:none;background:var(--bg)}
|
||||
#screen::-webkit-scrollbar{display:none}
|
||||
|
||||
.bottom-nav{height:60px;background:rgba(255,255,255,.92);backdrop-filter:blur(12px);border-top:1px solid rgba(0,0,0,.06);display:flex;align-items:center;justify-content:space-around;flex-shrink:0;z-index:100}
|
||||
[data-theme="dark"] .bottom-nav{background:rgba(31,41,55,.95)}
|
||||
.nav-item{display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer;padding:5px 4px;border-radius:10px;transition:all .18s;flex:1;position:relative}
|
||||
.nav-item svg{width:22px;height:22px;color:var(--muted);transition:color .18s}
|
||||
.nav-item span{font-size:9px;color:var(--muted);font-weight:500;transition:color .18s}
|
||||
.nav-item.active{background:rgba(0,62,126,.07)}
|
||||
.nav-item.active svg,.nav-item.active span{color:var(--accent)}
|
||||
.nav-item.active::after{content:'';position:absolute;bottom:4px;width:16px;height:3px;border-radius:2px;background:var(--accent);opacity:.6}
|
||||
|
||||
.page{padding:0 0 80px;min-height:100%}
|
||||
.card{background:var(--card);border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,.07);padding:16px;margin-bottom:12px}
|
||||
.section-label{text-transform:uppercase;font-size:11px;letter-spacing:.06em;color:var(--muted);margin:20px 16px 8px;font-weight:600}
|
||||
.hero-grad{background:linear-gradient(135deg,var(--accent) 0%,#005BB5 100%);padding:24px 20px 20px;color:#fff}
|
||||
.kpi-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:14px 16px}
|
||||
.kpi-card{background:var(--card);border-radius:16px;padding:14px 16px;box-shadow:0 2px 10px rgba(0,0,0,.07);border:1px solid rgba(0,0,0,.05)}
|
||||
.kpi-value{font-size:23px;font-weight:800;color:var(--ink);line-height:1;letter-spacing:-0.03em}
|
||||
.kpi-label{font-size:11px;color:var(--muted);margin-top:5px;font-weight:500}
|
||||
.kpi-delta{font-size:11px;font-weight:700;margin-top:7px}
|
||||
.kpi-delta.up{color:var(--success)}
|
||||
.kpi-delta.dn{color:var(--danger)}
|
||||
.stat-row{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.05)}
|
||||
.stat-row:last-child{border:none;padding-bottom:0}
|
||||
.stat-l{font-size:13px;color:var(--muted);font-weight:500}
|
||||
.stat-v{font-size:14px;font-weight:700;color:var(--ink)}
|
||||
.stat-v.g{color:var(--success)}
|
||||
.stat-v.r{color:var(--danger)}
|
||||
.stat-v.b{color:var(--accent)}
|
||||
.prog-bg{background:rgba(0,0,0,.07);border-radius:4px;height:6px;flex:1;margin:0 10px;overflow:hidden}
|
||||
.badge-sm{font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;white-space:nowrap}
|
||||
.bg{background:var(--s-success-bg);color:#065F46}
|
||||
.bw{background:var(--s-warning-bg);color:#92400E}
|
||||
.br{background:var(--s-danger-bg);color:#991B1B}
|
||||
.bb{background:var(--s-info-bg);color:#1D4ED8}
|
||||
.pill-row{display:flex;gap:6px;padding:0 16px;margin-bottom:12px;flex-wrap:wrap}
|
||||
.pill{padding:5px 12px;border-radius:20px;font-size:11px;font-weight:700;cursor:pointer;transition:all .18s;border:1.5px solid transparent}
|
||||
.pill.active{background:var(--accent);color:#fff}
|
||||
.pill.inactive{background:rgba(0,0,0,.05);color:var(--muted)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="crm-back-nav" style="position:fixed;top:0;left:0;right:0;z-index:9999;background:rgba(255,255,255,0.92);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border-bottom:1px solid rgba(0,0,0,.08);padding:8px 16px;display:flex;align-items:center">
|
||||
<a href="https://wasrusgen.github.io/wasrusgen1-crm/" style="display:inline-flex;align-items:center;gap:6px;font-family:Inter,system-ui,sans-serif;font-size:13px;font-weight:600;color:#003E7E;text-decoration:none;padding:4px 12px;border-radius:8px;background:#F0F4FF;transition:background .15s" onmouseover="this.style.background='#DDE8FF'" onmouseout="this.style.background='#F0F4FF'">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 5l-7 7 7 7"/></svg>
|
||||
← Все кабинеты</a>
|
||||
<span style="margin-left:12px;font-size:12px;color:#8A94A6;font-family:Inter,system-ui,sans-serif">Коммерческий директор</span>
|
||||
</div>
|
||||
<div style="height:44px"></div>
|
||||
<div id="controls">
|
||||
<label>Тема:</label>
|
||||
<div id="themeButtons">
|
||||
<button class="theme-btn active" data-t="zov" onclick="setTheme('zov',this)">Синяя</button>
|
||||
<button class="theme-btn" data-t="radar" onclick="setTheme('radar',this)">CRM</button>
|
||||
<button class="theme-btn" data-t="dark" onclick="setTheme('dark',this)">Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="phoneFrame">
|
||||
<div id="statusBar">
|
||||
<span>9:41</span>
|
||||
<div class="sb-r">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1.42 9a16 16 0 0 1 21.16 0M5 12.55a11 11 0 0 1 14.08 0M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01"/></svg>
|
||||
<svg width="18" height="12" viewBox="0 0 27 12"><rect x="0" y="0" width="6" height="12" rx="1" fill="currentColor" opacity=".35"/><rect x="8" y="0" width="6" height="12" rx="1" fill="currentColor" opacity=".55"/><rect x="16" y="0" width="6" height="12" rx="1" fill="currentColor"/><rect x="23" y="3" width="3" height="6" rx="1" fill="currentColor" opacity=".4"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div id="screen"></div>
|
||||
<div class="bottom-nav" id="nav"></div>
|
||||
</div>
|
||||
|
||||
<script src="data.js"></script>
|
||||
<script>
|
||||
// ── КАБИНЕТ: КОММЕРЧЕСКИЙ ДИРЕКТОР ───────────────────────────────────────────
|
||||
// Данные вертикали (_HIERARCHY, _CHESS_MGRS, _MONTHLY_STATS, _MGR_REQUESTS) — из data.js
|
||||
|
||||
window._sc = window._sc || 'home';
|
||||
window._ordFilter = window._ordFilter || 'all';
|
||||
window._ordSalon = window._ordSalon || 'all';
|
||||
window._prcFilter = window._prcFilter || 'pending';
|
||||
window._mgrExp = window._mgrExp || null;
|
||||
window._period = window._period || 'Май';
|
||||
window._prcDone = window._prcDone || {};
|
||||
|
||||
function _toast(msg,col){
|
||||
var t=document.createElement('div');
|
||||
t.textContent=msg;
|
||||
t.style.cssText='position:fixed;bottom:90px;left:50%;transform:translateX(-50%);background:'+(col||'#1A1A2E')+';color:#fff;padding:10px 20px;border-radius:20px;font-size:13px;font-weight:600;z-index:9999;white-space:nowrap;box-shadow:0 4px 20px rgba(0,0,0,.25)';
|
||||
document.body.appendChild(t);
|
||||
setTimeout(function(){t.style.opacity='0';t.style.transition='opacity .4s';},1800);
|
||||
setTimeout(function(){t.remove();},2200);
|
||||
}
|
||||
|
||||
function setTheme(t,btn){
|
||||
document.body.removeAttribute('data-theme');
|
||||
if(t!=='zov') document.body.setAttribute('data-theme',t);
|
||||
document.querySelectorAll('.theme-btn').forEach(b=>b.classList.remove('active'));
|
||||
if(btn) btn.classList.add('active');
|
||||
}
|
||||
|
||||
function _nav(s){window._sc=s;_render();}
|
||||
function _render(){
|
||||
var sc=document.getElementById('screen');
|
||||
sc.innerHTML=_rs();
|
||||
sc.scrollTop=0;
|
||||
document.getElementById('nav').innerHTML=_nb();
|
||||
}
|
||||
|
||||
// ─── ИКОНКИ ────────────────────────────────────────────────────────
|
||||
var _ICONS={
|
||||
chart:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/><line x1="2" y1="20" x2="22" y2="20"/></svg>',
|
||||
funnel:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>',
|
||||
list:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>',
|
||||
users:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
|
||||
bag:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>',
|
||||
wallet:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><circle cx="18" cy="14" r="1.5"/></svg>',
|
||||
check:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',
|
||||
alert:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><triangle/><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
|
||||
};
|
||||
|
||||
// ─── НАВ ────────────────────────────────────────────────────────────
|
||||
function _nb(){
|
||||
var items=[
|
||||
{id:'home', icon:'chart', label:'Главная'},
|
||||
{id:'funnel', icon:'funnel', label:'Воронка'},
|
||||
{id:'orders', icon:'list', label:'Заказы'},
|
||||
{id:'managers',icon:'users', label:'Персонал'},
|
||||
{id:'purchases',icon:'bag', label:'Закупки'},
|
||||
{id:'finance', icon:'wallet', label:'Финансы'},
|
||||
];
|
||||
return items.map(it=>'<div class="nav-item'+(window._sc===it.id?' active':'')+'" onclick="_nav(\''+it.id+'\')">'
|
||||
+_ICONS[it.icon]+'<span>'+it.label+'</span></div>').join('');
|
||||
}
|
||||
|
||||
function _rs(){
|
||||
if(window._sc==='home') return _sHome();
|
||||
if(window._sc==='funnel') return _sFunnel();
|
||||
if(window._sc==='orders') return _sOrders();
|
||||
if(window._sc==='managers') return _sManagers();
|
||||
if(window._sc==='purchases')return _sPurchases();
|
||||
if(window._sc==='finance') return _sFinance();
|
||||
return _sHome();
|
||||
}
|
||||
|
||||
// ─── ДАННЫЕ ─────────────────────────────────────────────────────────
|
||||
var _orders=[
|
||||
{id:'КЧ-2847', client:'ООО «РемСтрой»', sum:187000, stage:'mount', salon:'Ленина', mgr:'Дмитрий К.', date:'22 мая', overdue:false},
|
||||
{id:'ЗМ-2831', client:'Надежда Орлова', sum:142000, stage:'contract',salon:'Победы', mgr:'Ольга Р.', date:'19 мая', overdue:false},
|
||||
{id:'КЧ-2819', client:'ИП Сидоров', sum:128500, stage:'kp', salon:'Ленина', mgr:'Ольга Р.', date:'18 мая', overdue:false},
|
||||
{id:'ЗМ-2841', client:'Анна Белова', sum:113000, stage:'mount', salon:'Победы', mgr:'Ольга Р.', date:'14 мая', overdue:true},
|
||||
{id:'КЧ-2798', client:'Сергей Павлов', sum:98500, stage:'mount', salon:'Ленина', mgr:'Дмитрий К.', date:'13 мая', overdue:true},
|
||||
{id:'ЗМ-2856', client:'Марина Фролова', sum:87000, stage:'kp', salon:'Победы', mgr:'Дмитрий К.', date:'24 мая', overdue:false},
|
||||
{id:'КЧ-2861', client:'ООО «ДомСтрой»', sum:76500, stage:'contract',salon:'Ленина', mgr:'Дмитрий К.', date:'23 мая', overdue:false},
|
||||
{id:'ЗМ-2863', client:'Алексей Громов', sum:68000, stage:'lead', salon:'Победы', mgr:'Ольга Р.', date:'25 мая', overdue:false},
|
||||
{id:'КЧ-2867', client:'Татьяна Соколова', sum:54000, stage:'kp', salon:'Ленина', mgr:'Дмитрий К.', date:'26 мая', overdue:false},
|
||||
{id:'ЗМ-2871', client:'Виктор Попов', sum:48500, stage:'lead', salon:'Победы', mgr:'Ольга Р.', date:'27 мая', overdue:false},
|
||||
];
|
||||
|
||||
var _stageMap={
|
||||
lead: {label:'Лид', color:'#94A3B8', bg:'#F1F5F9'},
|
||||
kp: {label:'КП', color:'#F59E0B', bg:'#FFFBEB'},
|
||||
contract:{label:'Договор', color:'#3B82F6', bg:'#EFF6FF'},
|
||||
mount: {label:'Монтаж', color:'#10B981', bg:'#ECFDF5'},
|
||||
done: {label:'Выполнен', color:'#76BD22', bg:'#F0FDF4'},
|
||||
};
|
||||
|
||||
// ─── ГЛАВНАЯ ────────────────────────────────────────────────────────
|
||||
var _PERIODS={
|
||||
'Май': {plan:3200000, fact:2847000, orders:47, avgCheck:51800, overdue:2, newLeads:26, growth:18,
|
||||
convs:[{from:'Лид',to:'КП',pct:61,norm:65},{from:'КП',to:'Договор',pct:48,norm:55},{from:'Договор',to:'Монтаж',pct:91,norm:85}],
|
||||
sources:[{name:'Рекомендации',pct:42,cnt:11,color:'#10B981'},{name:'Авито',pct:28,cnt:7,color:'#3B82F6'},{name:'Instagram',pct:18,cnt:5,color:'#8B5CF6'},{name:'Сайт / SEO',pct:12,cnt:3,color:'#F59E0B'}]},
|
||||
'Апр': {plan:2900000, fact:2410000, orders:41, avgCheck:48600, overdue:1, newLeads:22, growth:8,
|
||||
convs:[{from:'Лид',to:'КП',pct:65,norm:65},{from:'КП',to:'Договор',pct:52,norm:55},{from:'Договор',to:'Монтаж',pct:88,norm:85}],
|
||||
sources:[{name:'Рекомендации',pct:38,cnt:9,color:'#10B981'},{name:'Авито',pct:32,cnt:7,color:'#3B82F6'},{name:'Instagram',pct:20,cnt:5,color:'#8B5CF6'},{name:'Сайт / SEO',pct:10,cnt:2,color:'#F59E0B'}]},
|
||||
'Мар': {plan:2700000, fact:2231000, orders:36, avgCheck:45300, overdue:3, newLeads:18, growth:-2,
|
||||
convs:[{from:'Лид',to:'КП',pct:58,norm:65},{from:'КП',to:'Договор',pct:45,norm:55},{from:'Договор',to:'Монтаж',pct:83,norm:85}],
|
||||
sources:[{name:'Рекомендации',pct:40,cnt:7,color:'#10B981'},{name:'Авито',pct:25,cnt:5,color:'#3B82F6'},{name:'Instagram',pct:22,cnt:4,color:'#8B5CF6'},{name:'Сайт / SEO',pct:13,cnt:2,color:'#F59E0B'}]},
|
||||
};
|
||||
|
||||
function _sHome(){
|
||||
var d=_PERIODS[window._period]||_PERIODS['Май'];
|
||||
var totalPlan=d.plan, totalFact=d.fact;
|
||||
var pct=Math.round(totalFact/totalPlan*100);
|
||||
var overdue=d.overdue;
|
||||
var overdueSum=_orders.filter(function(o){return o.overdue;}).reduce(function(s,o){return s+o.sum;},0);
|
||||
var sources=d.sources;
|
||||
var convs=d.convs;
|
||||
var periodLabels=['Май','Апр','Мар'];
|
||||
var prevP={'Май':'апрелю','Апр':'марту','Мар':'февралю'};
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div class="hero-grad">'
|
||||
// Период-свитчер внутри hero
|
||||
+ '<div style="display:flex;gap:6px;margin-bottom:14px">'
|
||||
+ periodLabels.map(function(p){
|
||||
var act=window._period===p;
|
||||
return '<div onclick="window._period=\''+p+'\';_render()" style="padding:5px 14px;border-radius:20px;font-size:11px;font-weight:700;cursor:pointer;'
|
||||
+(act?'background:rgba(255,255,255,.25);color:#fff':'background:rgba(255,255,255,.1);color:rgba(255,255,255,.6)')+'">'+p+'</div>';
|
||||
}).join('')
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:11px;font-weight:700;opacity:.65;text-transform:uppercase;letter-spacing:.08em;margin-bottom:6px">'+window._period.toUpperCase()+' 2026 · Коммерческий директор</div>'
|
||||
+ '<div style="font-size:28px;font-weight:800;letter-spacing:-.03em">'+totalFact.toLocaleString('ru')+' ₽</div>'
|
||||
+ '<div style="font-size:13px;opacity:.8;margin-top:4px">Выручка · <span style="color:'+(d.growth>=0?'#76BD22':'#FCA5A5')+';font-weight:700">'+(d.growth>=0?'▲ +':'▼ ')+Math.abs(d.growth)+'%</span> к '+prevP[window._period]+'</div>'
|
||||
+ '<div style="margin-top:14px">'
|
||||
+ '<div style="display:flex;justify-content:space-between;margin-bottom:5px">'
|
||||
+ '<span style="font-size:11px;opacity:.7">План: '+totalPlan.toLocaleString('ru')+' ₽</span>'
|
||||
+ '<span style="font-size:12px;font-weight:800;color:'+(pct>=90?'#76BD22':'#FCD34D')+'">'+pct+'%</span>'
|
||||
+ '</div>'
|
||||
+ '<div style="height:6px;background:rgba(255,255,255,.2);border-radius:3px;overflow:hidden">'
|
||||
+ '<div style="height:100%;width:'+pct+'%;background:'+(pct>=90?'#76BD22':'#FCD34D')+';border-radius:3px;transition:.4s"></div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">'+d.orders+'</div><div class="kpi-label">Заказов в работе</div><div class="kpi-delta up">план '+Math.round(d.plan/1000)+' тыс</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">'+Math.round(d.avgCheck/1000)+' тыс ₽</div><div class="kpi-label">Средний чек</div><div class="kpi-delta up">▲ факт</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value" style="color:var(--danger)">'+overdue+'</div><div class="kpi-label">Просрочено</div><div class="kpi-delta dn">'+Math.round(overdueSum/1000)+' тыс риск</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">'+d.newLeads+'</div><div class="kpi-label">Новых лидов</div><div class="kpi-delta up">за '+window._period+'</div></div>'
|
||||
+'</div>'
|
||||
|
||||
// Конверсии — быстрый взгляд
|
||||
+'<div class="section-label">Конверсии · '+window._period+'</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:14px 16px">'
|
||||
+convs.map(function(c){
|
||||
var ok=c.pct>=c.norm;
|
||||
return '<div style="display:flex;align-items:center;padding:7px 0;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="width:130px;font-size:12px;color:var(--muted)">'+c.from+' → '+c.to+'</div>'
|
||||
+'<div style="flex:1;height:5px;background:rgba(0,0,0,.07);border-radius:3px;overflow:hidden;margin:0 10px">'
|
||||
+ '<div style="height:100%;width:'+c.pct+'%;background:'+(ok?'var(--success)':'var(--danger)')+';border-radius:3px"></div>'
|
||||
+'</div>'
|
||||
+'<div style="font-size:13px;font-weight:800;color:'+(ok?'var(--success)':'var(--danger)')+'">'+c.pct+'%</div>'
|
||||
+'<div style="font-size:10px;color:var(--muted);margin-left:4px;width:46px">норма '+c.norm+'%</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
|
||||
// Источники лидов
|
||||
+'<div class="section-label">Источники лидов · '+window._period+'</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:14px 16px">'
|
||||
+sources.map(function(s){
|
||||
return '<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">'
|
||||
+'<div style="width:100px;font-size:12px;color:var(--muted);font-weight:500">'+s.name+'</div>'
|
||||
+'<div style="flex:1;height:6px;background:rgba(0,0,0,.07);border-radius:3px;overflow:hidden">'
|
||||
+ '<div style="height:100%;width:'+s.pct+'%;background:'+s.color+';border-radius:3px"></div>'
|
||||
+'</div>'
|
||||
+'<div style="font-size:12px;font-weight:700;color:var(--ink);width:28px;text-align:right">'+s.pct+'%</div>'
|
||||
+'<div style="font-size:11px;color:var(--muted);width:32px;text-align:right">'+s.cnt+' лид</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
|
||||
// Просроченные заказы
|
||||
+(overdue>0
|
||||
? '<div class="section-label">🔴 Просроченные заказы</div>'
|
||||
+'<div style="padding:0 16px">'
|
||||
+_orders.filter(o=>o.overdue).map(function(o){
|
||||
return '<div class="card" style="padding:12px 14px;margin-bottom:8px;border-left:3px solid var(--danger)">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:flex-start">'
|
||||
+ '<div>'
|
||||
+ '<div style="display:flex;align-items:center;gap:6px">'
|
||||
+ '<span style="font-size:12px;font-weight:700;color:var(--accent)">'+o.id+'</span>'
|
||||
+ '<span style="font-size:10px;font-weight:700;color:var(--danger)">⚠ просрочка</span>'
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:13px;font-weight:600;color:var(--ink);margin-top:2px">'+o.client+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">'+o.mgr+' · Салон '+o.salon+'</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="text-align:right">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">'+o.sum.toLocaleString('ru')+' ₽</div>'
|
||||
+ '<div style="font-size:10px;color:var(--muted);margin-top:2px">с '+o.date+'</div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
: '')
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── ВОРОНКА ────────────────────────────────────────────────────────
|
||||
function _sFunnel(){
|
||||
var stages=[
|
||||
{id:'lead', label:'Лиды', cnt:26, sum:1340000, norm:null, conv:null, normConv:null},
|
||||
{id:'kp', label:'КП', cnt:16, sum:877500, norm:null, conv:61, normConv:65},
|
||||
{id:'contract',label:'Договор', cnt:11, sum:624000, norm:null, conv:48, normConv:55},
|
||||
{id:'mount', label:'Монтаж', cnt:10, sum:568000, norm:null, conv:91, normConv:85},
|
||||
{id:'done', label:'Выполнен', cnt:47, sum:2847000, norm:null, conv:100, normConv:95},
|
||||
];
|
||||
var maxCnt=stages[0].cnt;
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Воронка продаж</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">Май 2026 · оба салона</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">26</div><div class="kpi-label">Лидов за месяц</div><div class="kpi-delta up">▲ +4 к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value" style="color:'+(48>=55?'var(--success)':'var(--danger)')+'">48%</div><div class="kpi-label">КП→Договор</div><div class="kpi-delta dn">норма 55%</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">47</div><div class="kpi-label">Выполнено</div><div class="kpi-delta up">▲ +6 к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">9.2</div><div class="kpi-label">Дней до договора</div><div class="kpi-delta up">▼ −1.3 дн</div></div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="section-label">Стадии воронки</div>'
|
||||
+'<div style="padding:0 16px">'
|
||||
+stages.map(function(st,i){
|
||||
var w=Math.round(st.cnt/maxCnt*100);
|
||||
var sc=_stageMap[st.id];
|
||||
var convOk=st.conv===null?null:st.conv>=st.normConv;
|
||||
return '<div style="margin-bottom:6px">'
|
||||
// Бар воронки
|
||||
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:4px">'
|
||||
+ '<div style="width:72px;font-size:11px;font-weight:700;color:'+sc.color+'">'+st.label+'</div>'
|
||||
+ '<div style="flex:1;height:32px;background:'+sc.bg+';border-radius:8px;overflow:hidden;position:relative">'
|
||||
+ '<div style="height:100%;width:'+w+'%;background:'+sc.color+'22;border-radius:8px;position:absolute"></div>'
|
||||
+ '<div style="position:absolute;left:10px;top:50%;transform:translateY(-50%);display:flex;align-items:baseline;gap:6px">'
|
||||
+ '<span style="font-size:15px;font-weight:800;color:'+sc.color+'">'+st.cnt+'</span>'
|
||||
+ '<span style="font-size:10px;color:var(--muted)">заказов</span>'
|
||||
+ '</div>'
|
||||
+ '<div style="position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:11px;font-weight:700;color:var(--muted)">'+Math.round(st.sum/1000)+' тыс ₽</div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
// Конверсия между стадиями
|
||||
+(i>0 && st.conv!==null
|
||||
? '<div style="display:flex;align-items:center;gap:6px;padding:0 0 4px 76px">'
|
||||
+ '<div style="width:1px;height:10px;background:rgba(0,0,0,.1);margin-left:16px"></div>'
|
||||
+ '<span style="font-size:11px;font-weight:700;color:'+(convOk?'var(--success)':'var(--danger)')+'">'+st.conv+'%</span>'
|
||||
+ '<span style="font-size:10px;color:var(--muted)">конверсия · норма '+st.normConv+'%</span>'
|
||||
+ '<span style="font-size:10px;font-weight:700;color:'+(convOk?'var(--success)':'var(--danger)')+'">'+( convOk?'✓':'↓ -'+(st.normConv-st.conv)+'пп')+'</span>'
|
||||
+'</div>'
|
||||
: '')
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
|
||||
// По салонам
|
||||
+'<div class="section-label">Воронка по салонам</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:14px 16px">'
|
||||
+[
|
||||
{name:'Салон Ленина', color:'#3B82F6', leads:15, kp:9, contracts:7, done:27, conv:47},
|
||||
{name:'Салон Победы', color:'#8B5CF6', leads:11, kp:7, contracts:4, done:20, conv:36},
|
||||
].map(function(s){
|
||||
return '<div style="margin-bottom:14px;padding-bottom:14px;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">'
|
||||
+ '<span style="font-size:13px;font-weight:700;color:'+s.color+'">'+s.name+'</span>'
|
||||
+ '<span style="font-size:11px;color:var(--muted)">КП→Дог: <b style="color:'+(s.conv>=55?'var(--success)':'var(--danger)')+'">'+s.conv+'%</b></span>'
|
||||
+'</div>'
|
||||
+'<div style="display:flex;gap:6px">'
|
||||
+[
|
||||
{l:'Лиды', v:s.leads, c:'#94A3B8'},
|
||||
{l:'КП', v:s.kp, c:'#F59E0B'},
|
||||
{l:'Дог.', v:s.contracts,c:'#3B82F6'},
|
||||
{l:'Сдано', v:s.done, c:'#10B981'},
|
||||
].map(function(st){
|
||||
return '<div style="flex:1;text-align:center">'
|
||||
+'<div style="font-size:16px;font-weight:800;color:'+st.c+'">'+st.v+'</div>'
|
||||
+'<div style="font-size:9px;color:var(--muted);margin-top:2px">'+st.l+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── ЗАКАЗЫ ─────────────────────────────────────────────────────────
|
||||
function _sOrders(){
|
||||
var stageFilters=[
|
||||
{id:'all', label:'Все'},
|
||||
{id:'overdue', label:'⚠ Просрочка'},
|
||||
{id:'mount', label:'Монтаж'},
|
||||
{id:'contract', label:'Договор'},
|
||||
{id:'kp', label:'КП'},
|
||||
{id:'lead', label:'Лид'},
|
||||
];
|
||||
var salons=[{id:'all',label:'Все салоны'},{id:'Ленина',label:'Ленина'},{id:'Победы',label:'Победы'}];
|
||||
|
||||
var filtered=_orders.filter(function(o){
|
||||
var sf=window._ordFilter;
|
||||
var ss=window._ordSalon;
|
||||
var stageOk = sf==='all' ? true : sf==='overdue' ? o.overdue : o.stage===sf;
|
||||
var salonOk = ss==='all' ? true : o.salon===ss;
|
||||
return stageOk && salonOk;
|
||||
});
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Заказы в работе</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">'+filtered.length+' из '+_orders.length+' · май 2026</div>'
|
||||
+'</div>'
|
||||
|
||||
// Фильтр по стадии
|
||||
+'<div style="overflow-x:auto;scrollbar-width:none;padding:10px 16px 0">'
|
||||
+'<div style="display:flex;gap:6px;width:max-content">'
|
||||
+stageFilters.map(function(f){
|
||||
var act=window._ordFilter===f.id;
|
||||
return '<div onclick="window._ordFilter=\''+f.id+'\';_render()" style="padding:5px 12px;border-radius:20px;font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap;'
|
||||
+(act?'background:var(--accent);color:#fff':'background:rgba(0,0,0,.06);color:var(--muted)')+'">'+f.label+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
|
||||
// Фильтр по салону
|
||||
+'<div style="display:flex;gap:6px;padding:8px 16px 12px">'
|
||||
+salons.map(function(s){
|
||||
var act=window._ordSalon===s.id;
|
||||
return '<div onclick="window._ordSalon=\''+s.id+'\';_render()" style="padding:4px 10px;border-radius:20px;font-size:11px;font-weight:600;cursor:pointer;'
|
||||
+(act?'background:var(--accent);color:#fff':'background:rgba(0,0,0,.06);color:var(--muted)')+'">'+s.label+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
|
||||
// Список
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+(filtered.length===0
|
||||
? '<div style="padding:24px;text-align:center;color:var(--muted);font-size:13px">Нет заказов по фильтру</div>'
|
||||
: filtered.map(function(o,i){
|
||||
var sc=_stageMap[o.stage];
|
||||
return '<div style="display:flex;align-items:center;padding:12px 14px;border-bottom:'+(i<filtered.length-1?'1px solid rgba(0,0,0,.05)':'none')+';cursor:pointer">'
|
||||
+'<div style="flex:1;min-width:0">'
|
||||
+ '<div style="display:flex;align-items:center;gap:6px">'
|
||||
+ '<span style="font-size:12px;font-weight:700;color:var(--accent)">'+o.id+'</span>'
|
||||
+ (o.overdue?'<span style="font-size:10px;font-weight:700;color:var(--danger)">⚠ просрочка</span>':'')
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:13px;font-weight:600;color:var(--ink);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+o.client+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">'+o.mgr+' · Салон '+o.salon+'</div>'
|
||||
+'</div>'
|
||||
+'<div style="text-align:right;flex-shrink:0;margin-left:8px">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">'+o.sum.toLocaleString('ru')+' ₽</div>'
|
||||
+ '<div style="display:inline-flex;margin-top:3px;background:'+sc.bg+';border-radius:6px;padding:2px 7px">'
|
||||
+ '<span style="font-size:10px;font-weight:700;color:'+sc.color+'">'+sc.label+'</span>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join(''))
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// _HIERARCHY, _CHESS_MGRS, _MONTHLY_STATS, _MGR_REQUESTS — из data.js
|
||||
window._mgrExpSalon = window._mgrExpSalon || null;
|
||||
|
||||
// ─── МЕНЕДЖЕРЫ ──────────────────────────────────────────────────────
|
||||
function _sManagers(){
|
||||
var admins=_HIERARCHY.map(function(h){
|
||||
return Object.assign({role:'Администратор'}, h);
|
||||
});
|
||||
var sc={ok:'var(--success)',bad:'var(--danger)',warn:'var(--warn)'};
|
||||
var bg={ok:'#F0FDF4',bad:'#FEF2F2',warn:'#FFFBEB'};
|
||||
var totOrders =admins.reduce(function(s,a){return s+a.orders;},0);
|
||||
var totPlan =admins.reduce(function(s,a){return s+a.ordersPlan;},0);
|
||||
var totRev =admins.reduce(function(s,a){return s+a.revenue;},0);
|
||||
var totRevPlan=admins.reduce(function(s,a){return s+a.revenuePlan;},0);
|
||||
var totOverdue=admins.reduce(function(s,a){return s+a.overdue;},0);
|
||||
var totPurch =admins.reduce(function(s,a){return s+a.purchases;},0);
|
||||
var revPct =Math.round(totRev/totRevPlan*100);
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Персонал</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">'+admins.length+' администратора · май 2026</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">'+totOrders+'</div><div class="kpi-label">Заказов всего</div>'
|
||||
+ '<div class="kpi-delta '+(totOrders>=totPlan?'up':'dn')+'">'+totOrders+' из '+totPlan+' по плану</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value" style="font-size:18px">'+(totRev/1000000).toFixed(1)+' млн</div>'
|
||||
+ '<div class="kpi-label">Выручка факт</div>'
|
||||
+ '<div class="kpi-delta '+(revPct>=100?'up':'dn')+'">'+revPct+'% от плана</div></div>'
|
||||
+'</div>'
|
||||
|
||||
+(totOverdue>0||totPurch>0
|
||||
? '<div style="padding:0 16px 4px"><div style="background:#FFFBEB;border-radius:12px;padding:11px 14px;display:flex;gap:16px;flex-wrap:wrap">'
|
||||
+(totOverdue>0?'<span style="font-size:12px;color:#92400E">⚠ Просрочено: <b>'+totOverdue+'</b> заказа</span>':'')
|
||||
+(totPurch>0?'<span style="font-size:12px;color:#1D4ED8">📋 Закупки: <b>'+totPurch+'</b> ждут</span>':'')
|
||||
+'</div></div>'
|
||||
: '')
|
||||
|
||||
+'<div class="section-label">Администраторы → Менеджеры</div>'
|
||||
+'<div style="padding:0 16px">'
|
||||
+admins.map(function(a){
|
||||
var ordPct =Math.round(a.orders/a.ordersPlan*100);
|
||||
var revPctA=Math.round(a.revenue/a.revenuePlan*100);
|
||||
var sColor =sc[a.status];
|
||||
var isExp = window._mgrExpSalon===a.salonId;
|
||||
var mgrRevTotal=a.managers.reduce(function(s,m){return s+m.revenue;},0);
|
||||
return '<div class="card" style="padding:0;margin-bottom:10px;border-left:3px solid '+sColor+';overflow:hidden">'
|
||||
|
||||
// ── Заголовок администратора ──
|
||||
+'<div style="padding:14px 16px 10px">'
|
||||
+'<div style="display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:10px">'
|
||||
+ '<div>'
|
||||
+ '<div style="display:flex;align-items:center;gap:8px">'
|
||||
+ '<div style="width:32px;height:32px;border-radius:50%;background:'+a.color+';display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:#fff">'+a.admin.short+'</div>'
|
||||
+ '<div>'
|
||||
+ '<div style="font-size:14px;font-weight:700;color:var(--ink)">'+a.admin.name+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted)">'+a.role+' · '+a.salon+'</div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="background:'+bg[a.status]+';color:'+sColor+';border-radius:20px;padding:3px 10px;font-size:11px;font-weight:600">'
|
||||
+ (a.status==='ok'?'✓ Норма':a.status==='warn'?'⚠ Внимание':'✕ Проблема')
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
|
||||
// ── KPI салона ──
|
||||
+'<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:10px">'
|
||||
+ '<div style="background:var(--bg);border-radius:10px;padding:9px 10px;text-align:center">'
|
||||
+ '<div style="font-size:20px;font-weight:800;color:var(--ink);line-height:1">'+a.orders+'</div>'
|
||||
+ '<div style="font-size:9px;color:var(--muted);margin-top:2px;text-transform:uppercase;letter-spacing:.04em">заказов</div>'
|
||||
+ '<div style="font-size:10px;color:'+(ordPct>=100?'var(--success)':'var(--danger)')+';font-weight:600;margin-top:1px">'+ordPct+'% плана</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="background:var(--bg);border-radius:10px;padding:9px 10px;text-align:center">'
|
||||
+ '<div style="font-size:15px;font-weight:800;color:var(--ink);line-height:1">'+(a.revenue/1000).toLocaleString()+' тыс</div>'
|
||||
+ '<div style="font-size:9px;color:var(--muted);margin-top:2px;text-transform:uppercase;letter-spacing:.04em">выручка ₽</div>'
|
||||
+ '<div style="font-size:10px;color:'+(revPctA>=100?'var(--success)':'var(--danger)')+';font-weight:600;margin-top:1px">'+revPctA+'% плана</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="background:var(--bg);border-radius:10px;padding:9px 10px;text-align:center">'
|
||||
+ '<div style="font-size:20px;font-weight:800;color:var(--accent);line-height:1">'+a.newLeads+'</div>'
|
||||
+ '<div style="font-size:9px;color:var(--muted);margin-top:2px;text-transform:uppercase;letter-spacing:.04em">лидов</div>'
|
||||
+ '<div style="font-size:10px;color:var(--muted);margin-top:1px">'+a.managers.length+' менедж.</div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
|
||||
+(a.overdue>0||a.purchases>0
|
||||
? '<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px">'
|
||||
+(a.overdue>0?'<div style="display:flex;align-items:center;gap:4px;background:#FEF2F2;border-radius:8px;padding:5px 10px">'
|
||||
+'<span style="font-size:11px">⚠</span>'
|
||||
+'<span style="font-size:11px;color:#991B1B">Просрочено: <b>'+a.overdue+'</b> · риск '+Math.round(a.overdueRisk/1000)+' тыс</span></div>':'')
|
||||
+(a.purchases>0?'<div style="display:flex;align-items:center;gap:4px;background:#EFF6FF;border-radius:8px;padding:5px 10px">'
|
||||
+'<span style="font-size:11px">📋</span>'
|
||||
+'<span style="font-size:11px;color:#1E40AF">Закупки: <b>'+a.purchases+'</b> заявки</span></div>':'')
|
||||
+'</div>'
|
||||
: '')
|
||||
|
||||
// ── Кнопка раскрыть менеджеров ──
|
||||
+'<div onclick="window._mgrExpSalon=(window._mgrExpSalon===\''+a.salonId+'\'?null:\''+a.salonId+'\');_render()" '
|
||||
+ 'style="display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:rgba(0,0,0,.03);border-radius:10px;cursor:pointer;border:1px solid var(--line)">'
|
||||
+ '<span style="font-size:12px;font-weight:700;color:var(--accent)">'+a.managers.length+' менеджера · '+Math.round(mgrRevTotal/1000)+' тыс ₽ выручка</span>'
|
||||
+ '<span style="font-size:14px;color:var(--muted)">'+(isExp?'▲':'▼')+'</span>'
|
||||
+'</div>'
|
||||
|
||||
+'</div>'
|
||||
|
||||
// ── Раскрытые менеджеры ──
|
||||
+(isExp
|
||||
? '<div style="background:#F8FAFF;border-top:1px solid var(--line);padding:12px 16px">'
|
||||
+'<div style="font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-bottom:10px">Менеджеры · '+a.salon+'</div>'
|
||||
+a.managers.map(function(m,mi){
|
||||
return '<div style="display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:'+(mi<a.managers.length-1?'1px solid rgba(0,0,0,.05)':'none')+'">'
|
||||
// Аватар
|
||||
+'<div style="width:34px;height:34px;border-radius:50%;background:'+m.color+';display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:#fff;flex-shrink:0">'+m.short+'</div>'
|
||||
+'<div style="flex:1;min-width:0">'
|
||||
+ '<div style="display:flex;align-items:center;gap:6px">'
|
||||
+ '<span style="font-size:13px;font-weight:700;color:var(--ink)">'+m.name+'</span>'
|
||||
+ (m.active?'<span style="font-size:9px;background:#ECFDF5;color:#065F46;border-radius:10px;padding:1px 6px;font-weight:700">online</span>':'<span style="font-size:9px;background:var(--bg);color:var(--muted);border-radius:10px;padding:1px 6px">offline</span>')
|
||||
+ '</div>'
|
||||
+ '<div style="display:flex;gap:12px;margin-top:4px">'
|
||||
+ '<span style="font-size:11px;color:var(--muted)">Визиты: <b style="color:var(--ink)">'+m.visits+'</b></span>'
|
||||
+ '<span style="font-size:11px;color:var(--muted)">Сделки: <b style="color:var(--ink)">'+m.deals+'</b></span>'
|
||||
+ '<span style="font-size:11px;color:var(--muted)">Конверсия: <b style="color:'+(m.conversion>=30?'var(--success)':'var(--warn)')+'">'+m.conversion+'%</b></span>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+'<div style="text-align:right;flex-shrink:0">'
|
||||
+ '<div style="font-size:13px;font-weight:800;color:var(--ink)">'+Math.round(m.revenue/1000)+' тыс</div>'
|
||||
+ '<div style="display:flex;align-items:center;gap:2px;justify-content:flex-end;margin-top:2px">'
|
||||
+ '<span style="color:#F59E0B;font-size:10px">★</span>'
|
||||
+ '<span style="font-size:11px;font-weight:700;color:var(--ink)">'+m.rating+'</span>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
: '')
|
||||
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── ЗАКУПКИ ─────────────────────────────────────────────────────────
|
||||
function _sPurchases(){
|
||||
var pending=[
|
||||
{id:'ЗАК-041', salon:'Ленина', who:'Анна М.', item:'Расходники (скотч, упаковка)', sum:4800, date:'26 мая', urgent:false},
|
||||
{id:'ЗАК-042', salon:'Победы', who:'Ирина С.', item:'Картридж + бумага А4 (3 пач)', sum:3200, date:'27 мая', urgent:false},
|
||||
{id:'ЗАК-043', salon:'Ленина', who:'Анна М.', item:'Чистящие средства · 12 позиций', sum:6700, date:'27 мая', urgent:true},
|
||||
];
|
||||
|
||||
// Расходы по категориям: апрель vs май
|
||||
var cats=[
|
||||
{name:'Расходники', apr:18400, may:44200, norm:20000},
|
||||
{name:'Хозтовары', apr:8200, may:9100, norm:10000},
|
||||
{name:'Канцелярия', apr:3100, may:2800, norm:4000},
|
||||
{name:'Реклама/ПОС', apr:12000, may:14500, norm:15000},
|
||||
];
|
||||
var maxVal=Math.max.apply(null, cats.map(function(c){return Math.max(c.apr,c.may);}));
|
||||
|
||||
// Аномалии
|
||||
var anomalies=cats.filter(function(c){return c.may>c.norm*1.2;});
|
||||
|
||||
var tabs=[{id:'pending',label:'На согласовании ('+pending.length+')'},{id:'analytics',label:'Аналитика'}];
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Закупки</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">Контроль расходов · май 2026</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value" style="color:var(--warn)">'+pending.length+'</div><div class="kpi-label">Ожидают решения</div><div class="kpi-delta dn">14 700 ₽ суммарно</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value" style="color:var(--danger)">'+anomalies.length+'</div><div class="kpi-label">Аномалий расходов</div><div class="kpi-delta dn">выше нормы на 20%+</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">74 300 ₽</div><div class="kpi-label">Расходов за май</div><div class="kpi-delta dn">▲ +34% к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">55 400 ₽</div><div class="kpi-label">Расходов за апрель</div><div class="kpi-delta up">В норме</div></div>'
|
||||
+'</div>'
|
||||
|
||||
// Таб-переключатель
|
||||
+'<div style="display:flex;padding:0 16px;gap:6px;margin-bottom:4px">'
|
||||
+tabs.map(function(t){
|
||||
var act=window._prcFilter===t.id;
|
||||
return '<div onclick="window._prcFilter=\''+t.id+'\';_render()" style="padding:7px 14px;border-radius:20px;font-size:11px;font-weight:700;cursor:pointer;'
|
||||
+(act?'background:var(--accent);color:#fff':'background:rgba(0,0,0,.06);color:var(--muted)')+'">'+t.label+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
|
||||
// Заявки на согласование
|
||||
+(window._prcFilter==='pending'
|
||||
? '<div style="padding:0 16px">'
|
||||
+pending.map(function(p){
|
||||
return '<div class="card" style="padding:14px;margin-bottom:8px;'+(p.urgent?'border-left:3px solid var(--warn)':'')+'">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px">'
|
||||
+ '<div>'
|
||||
+ '<div style="display:flex;align-items:center;gap:6px">'
|
||||
+ '<span style="font-size:12px;font-weight:700;color:var(--accent)">'+p.id+'</span>'
|
||||
+ (p.urgent?'<span style="font-size:10px;font-weight:700;color:var(--warn)">⚡ срочно</span>':'')
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:2px">Салон '+p.salon+' · '+p.who+' · '+p.date+'</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:15px;font-weight:800;color:var(--ink)">'+p.sum.toLocaleString('ru')+' ₽</div>'
|
||||
+'</div>'
|
||||
+'<div style="font-size:13px;color:var(--ink);margin-bottom:12px">'+p.item+'</div>'
|
||||
+(window._prcDone[p.id]
|
||||
? '<div style="padding:7px 12px;background:#ECFDF5;border-radius:10px;font-size:12px;font-weight:700;color:var(--success);text-align:center">'
|
||||
+(window._prcDone[p.id]==='ok'?'✅ Согласовано':'❌ Отклонено')
|
||||
+'</div>'
|
||||
: '<div style="display:flex;gap:8px">'
|
||||
+'<button onclick="window._prcDone[\''+p.id+'\']=\'ok\';_toast(\'✅ Согласовано: '+p.id+'\',\'#10B981\');_render()" style="flex:1;padding:8px;background:var(--success);color:#fff;border:none;border-radius:10px;font-size:12px;font-weight:700;cursor:pointer">✓ Согласовать</button>'
|
||||
+'<button onclick="window._prcDone[\''+p.id+'\']=\'no\';_toast(\'❌ Отклонено: '+p.id+'\',\'#EF4444\');_render()" style="flex:1;padding:8px;background:var(--s-danger-bg);color:var(--danger);border:none;border-radius:10px;font-size:12px;font-weight:700;cursor:pointer">✕ Отклонить</button>'
|
||||
+'</div>')
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
|
||||
// Аналитика расходов
|
||||
: '<div style="padding:0 16px">'
|
||||
|
||||
// Аномалии
|
||||
+(anomalies.length>0
|
||||
? '<div style="margin-bottom:12px">'
|
||||
+anomalies.map(function(a){
|
||||
var diff=Math.round((a.may-a.norm)/a.norm*100);
|
||||
return '<div style="display:flex;align-items:center;gap:10px;background:#FEF2F2;border-radius:12px;padding:10px 12px;margin-bottom:6px">'
|
||||
+'<span style="font-size:20px">🚨</span>'
|
||||
+'<div style="flex:1">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">'+a.name+': +'+diff+'% выше нормы</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">'+a.may.toLocaleString('ru')+' ₽ / норма '+a.norm.toLocaleString('ru')+' ₽</div>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
: '')
|
||||
|
||||
// График категорий апр vs май
|
||||
+'<div class="card" style="padding:14px 16px">'
|
||||
+ '<div style="font-size:12px;font-weight:700;color:var(--ink);margin-bottom:12px">Расходы по категориям</div>'
|
||||
+ '<div style="display:flex;gap:12px;margin-bottom:8px">'
|
||||
+ '<div style="display:flex;align-items:center;gap:5px"><span style="width:8px;height:8px;border-radius:2px;background:#94A3B8;display:inline-block"></span><span style="font-size:10px;color:var(--muted)">Апрель</span></div>'
|
||||
+ '<div style="display:flex;align-items:center;gap:5px"><span style="width:8px;height:8px;border-radius:2px;background:var(--accent);display:inline-block"></span><span style="font-size:10px;color:var(--muted)">Май</span></div>'
|
||||
+ '<div style="display:flex;align-items:center;gap:5px"><span style="width:8px;height:8px;border-radius:2px;background:var(--warn);display:inline-block;opacity:.5"></span><span style="font-size:10px;color:var(--muted)">Норма</span></div>'
|
||||
+ '</div>'
|
||||
+cats.map(function(c){
|
||||
var aprW=Math.round(c.apr/maxVal*100);
|
||||
var mayW=Math.round(c.may/maxVal*100);
|
||||
var normW=Math.round(c.norm/maxVal*100);
|
||||
var over=c.may>c.norm*1.2;
|
||||
return '<div style="margin-bottom:12px">'
|
||||
+'<div style="display:flex;justify-content:space-between;margin-bottom:4px">'
|
||||
+ '<span style="font-size:12px;font-weight:600;color:var(--ink)">'+c.name+'</span>'
|
||||
+ '<span style="font-size:11px;font-weight:700;color:'+(over?'var(--danger)':'var(--muted)')+'">'+c.may.toLocaleString('ru')+' ₽'+(over?' ⚠':'')+'</span>'
|
||||
+'</div>'
|
||||
// Апрель
|
||||
+'<div style="height:5px;background:rgba(0,0,0,.06);border-radius:3px;margin-bottom:3px;overflow:hidden">'
|
||||
+ '<div style="height:100%;width:'+aprW+'%;background:#94A3B8;border-radius:3px"></div>'
|
||||
+'</div>'
|
||||
// Май
|
||||
+'<div style="height:5px;background:rgba(0,0,0,.06);border-radius:3px;margin-bottom:3px;overflow:hidden">'
|
||||
+ '<div style="height:100%;width:'+mayW+'%;background:'+(over?'var(--danger)':'var(--accent)')+';border-radius:3px"></div>'
|
||||
+'</div>'
|
||||
// Норма-маркер
|
||||
+'<div style="position:relative;height:2px">'
|
||||
+ '<div style="position:absolute;left:'+normW+'%;top:-4px;width:2px;height:10px;background:var(--warn);border-radius:1px"></div>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
+'</div>')
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── ФИНАНСЫ ────────────────────────────────────────────────────────
|
||||
function _sFinance(){
|
||||
var salons=[
|
||||
{name:'Салон Ленина', color:'#3B82F6', plan:1700000, fact:1537000, prev:1280000},
|
||||
{name:'Салон Победы', color:'#8B5CF6', plan:1500000, fact:1310000, prev:1180000},
|
||||
];
|
||||
var debitors=[
|
||||
{name:'ООО «ДомСтрой»', sum:89000, days:21, mgr:'Дмитрий К.'},
|
||||
{name:'Козлов А.С.', sum:54000, days:18, mgr:'Ольга Р.'},
|
||||
{name:'Белова Н.В.', sum:38000, days:16, mgr:'Ольга Р.'},
|
||||
{name:'Прочие (4)', sum:106000,days:14, mgr:'—'},
|
||||
];
|
||||
var totalDebt=debitors.reduce(function(s,d){return s+d.sum;},0);
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Финансы</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">План / Факт · май 2026</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">2 847 000 ₽</div><div class="kpi-label">Выручка факт</div><div class="kpi-delta dn">▼ −11% к плану</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">3 200 000 ₽</div><div class="kpi-label">Выручка план</div><div class="kpi-delta up">▲ +18% к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value" style="color:var(--danger)">'+Math.round(totalDebt/1000)+' тыс</div><div class="kpi-label">Дебиторка</div><div class="kpi-delta dn">7 клиентов</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">51 800 ₽</div><div class="kpi-label">Средний чек</div><div class="kpi-delta up">▲ +3 200 ₽</div></div>'
|
||||
+'</div>'
|
||||
|
||||
// План/факт по салонам
|
||||
+'<div class="section-label">Выполнение плана по салонам</div>'
|
||||
+'<div style="padding:0 16px">'
|
||||
+salons.map(function(s){
|
||||
var pct=Math.round(s.fact/s.plan*100);
|
||||
var growth=Math.round((s.fact-s.prev)/s.prev*100);
|
||||
return '<div class="card" style="padding:14px 16px;margin-bottom:10px">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:10px">'
|
||||
+ '<div>'
|
||||
+ '<div style="font-size:14px;font-weight:700;color:'+s.color+'">'+s.name+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:2px">Факт: <b style="color:var(--ink)">'+s.fact.toLocaleString('ru')+' ₽</b></div>'
|
||||
+ '</div>'
|
||||
+ '<div style="text-align:right">'
|
||||
+ '<div style="font-size:20px;font-weight:800;color:'+(pct>=95?'var(--success)':pct>=80?'var(--warn)':'var(--danger)')+'">'+pct+'%</div>'
|
||||
+ '<div style="font-size:10px;color:var(--muted)">от плана</div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
// Прогресс
|
||||
+'<div style="height:8px;background:rgba(0,0,0,.07);border-radius:4px;overflow:hidden;margin-bottom:6px">'
|
||||
+ '<div style="height:100%;width:'+Math.min(pct,100)+'%;background:'+s.color+';border-radius:4px;transition:.4s"></div>'
|
||||
+'</div>'
|
||||
+'<div style="display:flex;justify-content:space-between">'
|
||||
+ '<span style="font-size:10px;color:var(--muted)">План: '+s.plan.toLocaleString('ru')+' ₽</span>'
|
||||
+ '<span style="font-size:10px;font-weight:700;color:'+(growth>0?'var(--success)':'var(--danger)')+'">'+( growth>0?'▲ +':'')+growth+'% к апр.</span>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
|
||||
// P&L
|
||||
+'<div class="section-label">P&L · май 2026</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:14px 16px">'
|
||||
+[
|
||||
{label:'Выручка', v:2847000, prev:2410000, type:'rev'},
|
||||
{label:'Себестоимость', v:-1280000,prev:-1090000,type:'cost'},
|
||||
{label:'Валовая прибыль', v:1567000, prev:1320000, type:'profit',bold:true},
|
||||
{label:'Операц. расходы', v:-418000, prev:-380000, type:'cost'},
|
||||
{label:'Зарплата и %', v:-512000, prev:-442000, type:'cost'},
|
||||
{label:'EBITDA', v:637000, prev:498000, type:'ebitda',bold:true},
|
||||
].map(function(row,i){
|
||||
var delta=row.prev?Math.round((row.v-row.prev)/Math.abs(row.prev)*100):0;
|
||||
var isPos=row.v>=0;
|
||||
var color=row.type==='profit'||row.type==='ebitda'?'var(--accent)':row.type==='rev'?'var(--success)':'var(--muted)';
|
||||
return '<div style="display:flex;align-items:center;padding:8px 0;border-bottom:'+(i<5?'1px solid rgba(0,0,0,.05)':'none')+'">'
|
||||
+'<div style="flex:1;font-size:'+(row.bold?'13':'12')+'px;font-weight:'+(row.bold?'700':'500')+';color:'+(row.bold?'var(--ink)':'var(--muted)')+'">'+row.label+'</div>'
|
||||
+'<div style="font-size:'+(row.bold?'14':'13')+'px;font-weight:'+(row.bold?'800':'600')+';color:'+color+';margin-right:10px">'+(isPos?'':'')+Math.abs(row.v).toLocaleString('ru')+' ₽</div>'
|
||||
+'<div style="font-size:10px;font-weight:700;color:'+(delta>=0?'var(--success)':'var(--danger)')+';width:46px;text-align:right">'+(delta>=0?'▲ +':'▼ ')+Math.abs(delta)+'%</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
|
||||
// Маржинальность
|
||||
+'<div style="padding:0 16px 8px"><div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px">'
|
||||
+[
|
||||
{label:'Валов. маржа',v:'55%',ok:true},
|
||||
{label:'EBITDA маржа',v:'22%',ok:true},
|
||||
{label:'Рост к апр.',v:'+18%',ok:true},
|
||||
].map(function(m){
|
||||
return '<div style="background:'+(m.ok?'#ECFDF5':'#FEF2F2')+';border-radius:12px;padding:10px;text-align:center">'
|
||||
+'<div style="font-size:18px;font-weight:800;color:'+(m.ok?'var(--success)':'var(--danger)')+'">'+m.v+'</div>'
|
||||
+'<div style="font-size:9px;color:var(--muted);margin-top:2px;font-weight:600">'+m.label+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
|
||||
// Дебиторка
|
||||
+'<div class="section-label">Дебиторская задолженность · '+totalDebt.toLocaleString('ru')+' ₽</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+debitors.map(function(d,i){
|
||||
var urgentColor=d.days>=21?'var(--danger)':d.days>=16?'var(--warn)':'var(--muted)';
|
||||
return '<div style="display:flex;align-items:center;padding:12px 14px;border-bottom:'+(i<debitors.length-1?'1px solid rgba(0,0,0,.05)':'none')+'">'
|
||||
+'<div style="flex:1;min-width:0">'
|
||||
+ '<div style="font-size:13px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+d.name+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">'+d.mgr+'</div>'
|
||||
+'</div>'
|
||||
+'<div style="text-align:right;flex-shrink:0;margin-left:8px">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">'+d.sum.toLocaleString('ru')+' ₽</div>'
|
||||
+ '<div style="font-size:10px;font-weight:700;color:'+urgentColor+';margin-top:1px">'+d.days+' дней</div>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── СТАРТ ──────────────────────────────────────────────────────────
|
||||
window.addEventListener('DOMContentLoaded', function(){_render();});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
4262
Mokap/mockup_manager.html
Normal file
4262
Mokap/mockup_manager.html
Normal file
File diff suppressed because it is too large
Load Diff
1305
Mokap/mockup_measurer.html
Normal file
1305
Mokap/mockup_measurer.html
Normal file
File diff suppressed because it is too large
Load Diff
918
Mokap/mockup_owner.html
Normal file
918
Mokap/mockup_owner.html
Normal file
@ -0,0 +1,918 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@wasrusgen1 CRM — Генеральный директор</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Montserrat:wght@700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{background:#C8CACD;min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px;font-family:'Inter',sans-serif}
|
||||
|
||||
body{--accent:#003E7E;--accent2:#76BD22;--bg:#F5F6F8;--card:#FFFFFF;--ink:#1A1A2E;--muted:#8A94A6;--danger:#EF4444;--warn:#F59E0B;--success:#10B981;--s-success-bg:#ECFDF5;--s-warning-bg:#FFFBEB;--s-danger-bg:#FEF2F2;--s-info:#3B82F6;--s-info-bg:#EFF6FF}
|
||||
body[data-theme="radar"]{--accent:#4338CA;--accent2:#6366F1;--bg:#F9FAFB;--card:#FFFFFF;--ink:#111827;--muted:#6B7280}
|
||||
body[data-theme="dark"]{--accent:#4338CA;--accent2:#6366F1;--bg:#111827;--card:#1F2937;--ink:#F9FAFB;--muted:#9CA3AF}
|
||||
|
||||
#controls{display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap;justify-content:center;width:100%;max-width:600px}
|
||||
#controls label{color:#fff;font-size:13px;font-weight:600}
|
||||
#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:linear-gradient(135deg,#1E1B4B,#4338CA);color:#fff}
|
||||
.theme-btn[data-t="dark"]{background:#111827;color:#6366F1}
|
||||
|
||||
#phoneFrame{width:390px;height:844px;background:var(--bg);border-radius:44px;overflow:hidden;box-shadow:0 24px 80px rgba(0,0,0,.4),inset 0 0 0 1px rgba(255,255,255,.15);position:relative;display:flex;flex-direction:column}
|
||||
#statusBar{height:44px;background:var(--card);display:flex;align-items:center;justify-content:space-between;padding:0 24px;flex-shrink:0;font-size:13px;font-weight:600;color:var(--ink);z-index:10}
|
||||
.sb-r{display:flex;align-items:center;gap:6px}
|
||||
#screen{flex:1;overflow-y:auto;overflow-x:hidden;scrollbar-width:none;background:var(--bg)}
|
||||
#screen::-webkit-scrollbar{display:none}
|
||||
|
||||
.bottom-nav{height:60px;background:rgba(255,255,255,.92);backdrop-filter:blur(12px);border-top:1px solid rgba(0,0,0,.06);display:flex;align-items:center;justify-content:space-around;flex-shrink:0;z-index:100}
|
||||
[data-theme="dark"] .bottom-nav{background:rgba(31,41,55,.95)}
|
||||
.nav-item{display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer;padding:5px 6px;border-radius:10px;transition:all .18s;flex:1;position:relative}
|
||||
.nav-item svg{width:22px;height:22px;color:var(--muted);transition:color .18s}
|
||||
.nav-item span{font-size:10px;color:var(--muted);font-weight:500;transition:color .18s}
|
||||
.nav-item.active{background:rgba(0,62,126,.07)}
|
||||
.nav-item.active svg,.nav-item.active span{color:var(--accent)}
|
||||
.nav-item.active::after{content:'';position:absolute;bottom:4px;width:16px;height:3px;border-radius:2px;background:var(--accent);opacity:.6}
|
||||
[data-theme="radar"] .nav-item.active{background:rgba(67,56,202,.07)}
|
||||
[data-theme="dark"] .nav-item.active{background:rgba(99,102,241,.12)}
|
||||
|
||||
.page{padding:0 0 80px;min-height:100%}
|
||||
.card{background:var(--card);border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,.07);padding:16px;margin-bottom:12px}
|
||||
.card.warn-b{border-left:4px solid var(--warn)}
|
||||
.card.ok-b{border-left:4px solid var(--success)}
|
||||
.card.danger-b{border-left:4px solid var(--danger)}
|
||||
.card.accent-b{border-left:4px solid var(--accent)}
|
||||
|
||||
.section-label{text-transform:uppercase;font-size:11px;letter-spacing:.06em;color:var(--muted);margin:20px 16px 8px;font-weight:600}
|
||||
|
||||
.hero-grad{background:linear-gradient(135deg,var(--accent) 0%,#005BB5 100%);padding:24px 20px 20px;color:#fff}
|
||||
[data-theme="radar"] .hero-grad{background:linear-gradient(135deg,#312E81 0%,#4338CA 100%)}
|
||||
[data-theme="dark"] .hero-grad{background:linear-gradient(135deg,#1E293B 0%,#312E81 100%)}
|
||||
|
||||
.kpi-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:14px 16px}
|
||||
.kpi-card{background:var(--card);border-radius:16px;padding:14px 16px;box-shadow:0 2px 10px rgba(0,0,0,.07);border:1px solid rgba(0,0,0,.05)}
|
||||
.kpi-value{font-size:23px;font-weight:800;color:var(--ink);line-height:1;letter-spacing:-0.03em}
|
||||
.kpi-label{font-size:11px;color:var(--muted);margin-top:5px;font-weight:500;letter-spacing:.01em}
|
||||
.kpi-delta{font-size:11px;font-weight:700;margin-top:7px;letter-spacing:.01em}
|
||||
.kpi-delta.up{color:var(--success)}
|
||||
.kpi-delta.dn{color:var(--danger)}
|
||||
|
||||
.bar-wrap{display:flex;align-items:flex-end;gap:6px;height:72px;padding:0 16px}
|
||||
.bar{flex:1;border-radius:4px 4px 0 0}
|
||||
.bar.cur{background:var(--accent)}
|
||||
.bar.prev{background:var(--accent);opacity:.25}
|
||||
.bar-labels{display:flex;gap:6px;padding:4px 16px 0}
|
||||
.bar-label{flex:1;text-align:center;font-size:9px;color:var(--muted);font-weight:500}
|
||||
|
||||
.row-item{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid rgba(0,0,0,.05)}
|
||||
.row-item:last-child{border:none}
|
||||
.avatar-sm{width:38px;height:38px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:17px;flex-shrink:0}
|
||||
.ri-name{font-size:14px;font-weight:600;color:var(--ink)}
|
||||
.ri-sub{font-size:12px;color:var(--muted);margin-top:2px}
|
||||
.badge-sm{font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;white-space:nowrap}
|
||||
.bg{background:var(--s-success-bg);color:#065F46}
|
||||
.bw{background:var(--s-warning-bg);color:#92400E}
|
||||
.br{background:var(--s-danger-bg);color:#991B1B}
|
||||
.bb{background:var(--s-info-bg);color:#1D4ED8}
|
||||
|
||||
.stat-row{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.05)}
|
||||
.stat-row:last-child{border:none;padding-bottom:0}
|
||||
.stat-l{font-size:13px;color:var(--muted);font-weight:500}
|
||||
.stat-v{font-size:14px;font-weight:700;color:var(--ink)}
|
||||
.stat-v.g{color:var(--success)}
|
||||
.stat-v.r{color:var(--danger)}
|
||||
.stat-v.b{color:var(--accent)}
|
||||
|
||||
.prog-bg{background:rgba(0,0,0,.07);border-radius:4px;height:6px;flex:1;margin:0 10px;overflow:hidden}
|
||||
.prog-fill{height:100%;border-radius:4px;background:var(--accent2)}
|
||||
|
||||
.btn-p{width:100%;padding:14px;background:var(--accent);color:#fff;border:none;border-radius:12px;font-size:15px;font-weight:700;cursor:pointer;margin-top:12px}
|
||||
.btn-s{width:100%;padding:10px;background:transparent;color:var(--accent);border:1.5px solid var(--accent);border-radius:12px;font-size:13px;font-weight:600;cursor:pointer;margin-top:8px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="controls">
|
||||
<label>Тема:</label>
|
||||
<div id="themeButtons">
|
||||
<button class="theme-btn active" data-t="zov" onclick="setTheme('zov',this)">Синяя</button>
|
||||
<button class="theme-btn" data-t="radar" onclick="setTheme('radar',this)">CRM</button>
|
||||
<button class="theme-btn" data-t="dark" onclick="setTheme('dark',this)">Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="phoneFrame">
|
||||
<div id="statusBar">
|
||||
<span>9:41</span>
|
||||
<div class="sb-r">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1.42 9a16 16 0 0 1 21.16 0M5 12.55a11 11 0 0 1 14.08 0M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01"/></svg>
|
||||
<svg width="18" height="12" viewBox="0 0 27 12"><rect x="0" y="0" width="6" height="12" rx="1" fill="currentColor" opacity=".35"/><rect x="8" y="0" width="6" height="12" rx="1" fill="currentColor" opacity=".55"/><rect x="16" y="0" width="6" height="12" rx="1" fill="currentColor"/><rect x="23" y="3" width="3" height="6" rx="1" fill="currentColor" opacity=".4"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div id="screen"></div>
|
||||
<div class="bottom-nav" id="nav"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window._sc = window._sc || 'home';
|
||||
|
||||
function setTheme(t,btn){
|
||||
document.body.removeAttribute('data-theme');
|
||||
if(t!=='zov') document.body.setAttribute('data-theme',t);
|
||||
document.querySelectorAll('.theme-btn').forEach(b=>b.classList.remove('active'));
|
||||
if(btn) btn.classList.add('active');
|
||||
}
|
||||
|
||||
function _nav(s){window._sc=s;_render();}
|
||||
|
||||
function _render(){
|
||||
var sc=document.getElementById('screen');
|
||||
sc.innerHTML=_rs();
|
||||
sc.scrollTop=0;
|
||||
document.getElementById('nav').innerHTML=_nb();
|
||||
}
|
||||
|
||||
window._finTab = window._finTab || 'fact';
|
||||
window._probExp = window._probExp || null;
|
||||
if(window._monthDrill===undefined) window._monthDrill=null; // 0-5 = selected bar, null = closed
|
||||
if(!window._drillSalon) window._drillSalon='all'; // 'all'|'s1'|'s2'
|
||||
if(!window._salonVis) window._salonVis={s1:true,s2:true}; // toggle per salon
|
||||
|
||||
var _ICONS = {
|
||||
// BarChart2 — floor line + clean bars
|
||||
chart:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/><line x1="2" y1="20" x2="22" y2="20"/></svg>',
|
||||
truck:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M5 17H3a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v3"/><rect x="9" y="11" width="14" height="10" rx="1"/><circle cx="12" cy="21" r="1"/><circle cx="20" cy="21" r="1"/></svg>',
|
||||
// Wallet — clean, stroked coin slot
|
||||
wallet:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><circle cx="18" cy="14" r="1.5"/></svg>',
|
||||
// Users — two figures, cleaner arcs
|
||||
users:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
|
||||
// User single
|
||||
person:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
|
||||
// Sliders — modern "settings" / controls icon
|
||||
gear:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg>',
|
||||
// TrendingUp — clean arrow
|
||||
trend:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/></svg>'
|
||||
};
|
||||
|
||||
// ─── ДАННЫЕ МЕСЯЦЕВ (с разбивкой по салонам) ───────────────────────
|
||||
var _mdata=[
|
||||
{m:'Дек',v:1.42,s1:.62,d:31,seed:1},
|
||||
{m:'Янв',v:1.78,s1:.58,d:31,seed:2},
|
||||
{m:'Фев',v:2.08,s1:.55,d:28,seed:3},
|
||||
{m:'Мар',v:1.91,s1:.60,d:31,seed:4},
|
||||
{m:'Апр',v:2.41,s1:.57,d:30,seed:5},
|
||||
{m:'Май',v:2.85,s1:.54,d:31,seed:6},
|
||||
];
|
||||
|
||||
function _genDaily(seed,totalM,ndays){
|
||||
var r=[]; for(var i=0;i<ndays;i++){var s=(Math.sin(seed*31+i*17+3)*0.5+0.5)*0.8+0.1;r.push(Math.round(s*((i+seed)%7>=5?.28:1.18)*100000+18000));}
|
||||
var sum=r.reduce((a,b)=>a+b,0); var sc=totalM*1e6/sum; return r.map(d=>Math.round(d*sc));
|
||||
}
|
||||
|
||||
function _dayChart(days,color,idx){
|
||||
var W=330,H=68,n=days.length;
|
||||
var mx=Math.max.apply(null,days),mn=Math.min(mx*.02,Math.min.apply(null,days));
|
||||
var rng=mx-mn||1;
|
||||
var pts=days.map((v,i)=>[(Math.round(i/(n-1)*(W-12)+6)),Math.round(H-6-(v-mn)/rng*(H-18))]);
|
||||
var line=pts.map(p=>p[0]+','+p[1]).join(' ');
|
||||
var area='6,'+(H-6)+' '+line+' '+(W-6)+','+(H-6);
|
||||
var bi=days.indexOf(mx); var bp=pts[bi];
|
||||
return '<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;height:'+H+'px;display:block">'
|
||||
+'<defs><linearGradient id="dcg'+idx+'" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="'+color+'" stop-opacity=".2"/><stop offset="100%" stop-color="'+color+'" stop-opacity="0"/></linearGradient></defs>'
|
||||
+'<polygon points="'+area+'" fill="url(#dcg'+idx+')"/>'
|
||||
+'<polyline points="'+line+'" fill="none" stroke="'+color+'" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/>'
|
||||
+'<circle cx="'+bp[0]+'" cy="'+bp[1]+'" r="5" fill="'+color+'" opacity=".2"/>'
|
||||
+'<circle cx="'+bp[0]+'" cy="'+bp[1]+'" r="3" fill="'+color+'"/>'
|
||||
+'<text x="'+Math.min(bp[0]+6,W-40)+'" y="'+(bp[1]-6)+'" font-size="9" font-weight="800" fill="'+color+'">'+Math.round(mx/1000)+'тыс</text>'
|
||||
+'</svg>';
|
||||
}
|
||||
|
||||
function _drillPanel(idx){
|
||||
var md=_mdata[idx];
|
||||
var all=_genDaily(md.seed,md.v,md.d);
|
||||
var s1=all.map(d=>Math.round(d*md.s1));
|
||||
var s2=all.map((d,i)=>d-s1[i]);
|
||||
var clrs={all:'#003E7E',s1:'#3B82F6',s2:'#8B5CF6'};
|
||||
var days=window._drillSalon==='s1'?s1:window._drillSalon==='s2'?s2:all;
|
||||
var clr=clrs[window._drillSalon]||'#003E7E';
|
||||
var tot=days.reduce((a,b)=>a+b,0);
|
||||
var mx=Math.max.apply(null,days);
|
||||
var bestDay=days.indexOf(mx)+1;
|
||||
var pills=[{k:'all',l:'Все салоны'},{k:'s1',l:'Салон Ленина'},{k:'s2',l:'Салон Победы'}]
|
||||
.map(f=>{var a=window._drillSalon===f.k;return '<span onclick="window._drillSalon=\''+f.k+'\';_render()" style="display:inline-flex;padding:5px 12px;border-radius:20px;font-size:11px;font-weight:700;cursor:pointer;margin-right:6px;margin-bottom:6px;background:'+(a?clr:'rgba(0,0,0,.07)')+';color:'+(a?'#fff':'var(--muted)')+'">'+f.l+'</span>';}).join('');
|
||||
var t1=s1.reduce((a,b)=>a+b,0),t2=s2.reduce((a,b)=>a+b,0),tAll=all.reduce((a,b)=>a+b,0);
|
||||
return '<div style="border-top:2px solid rgba(0,62,126,.12);padding:14px 16px 4px;background:rgba(0,62,126,.03)">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">'
|
||||
+ '<div style="font-size:14px;font-weight:800;color:var(--ink)">'+md.m+' — по дням</div>'
|
||||
+ '<span onclick="window._monthDrill=null;_render()" style="width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;background:rgba(0,0,0,.08);border-radius:50%;font-size:16px;color:var(--muted);cursor:pointer">×</span>'
|
||||
+'</div>'
|
||||
+'<div style="margin-bottom:8px">'+pills+'</div>'
|
||||
+_dayChart(days,clr,idx)
|
||||
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;margin-top:10px">'
|
||||
+[{l:'Выручка',v:(tot/1e6).toFixed(2)+' М ₽'},{l:'Среднедн.',v:Math.round(tot/days.length/1000)+' тыс ₽'},{l:'Пик · '+bestDay+' '+md.m,v:Math.round(mx/1000)+' тыс ₽'}]
|
||||
.map(s=>'<div style="background:var(--card);border-radius:10px;padding:8px 10px;border:1px solid rgba(0,0,0,.06)"><div style="font-size:10px;color:var(--muted);font-weight:500;margin-bottom:3px">'+s.l+'</div><div style="font-size:12px;font-weight:800;color:var(--ink);letter-spacing:-.02em">'+s.v+'</div></div>')
|
||||
.join('')+'</div>'
|
||||
+(window._drillSalon==='all'
|
||||
?'<div style="margin-top:10px">'
|
||||
+[{l:'Салон Ленина',v:t1,c:'#3B82F6'},{l:'Салон Победы',v:t2,c:'#8B5CF6'}].map(s=>{var pct=Math.round(s.v/tAll*100);return '<div style="margin-bottom:8px"><div style="display:flex;justify-content:space-between;margin-bottom:4px"><span style="font-size:12px;color:var(--ink);font-weight:600">'+s.l+'</span><span style="font-size:12px;font-weight:700;color:var(--ink)">'+(s.v/1e6).toFixed(2)+' М · '+pct+'%</span></div><div style="height:5px;background:rgba(0,0,0,.07);border-radius:3px"><div style="height:100%;background:'+s.c+';border-radius:3px;width:'+pct+'%"></div></div></div>';}).join('')
|
||||
+'</div>'
|
||||
:'')
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
function _nb(){
|
||||
var items=[
|
||||
{id:'home', icon:'chart', label:'Главная'},
|
||||
{id:'sales', icon:'trend', label:'Продажи'},
|
||||
{id:'finance', icon:'wallet',label:'Финансы'},
|
||||
{id:'team', icon:'users', label:'Команда'},
|
||||
{id:'sklad', icon:'truck', label:'Склад'},
|
||||
{id:'settings',icon:'gear', label:'Настройки'},
|
||||
];
|
||||
return items.map(it=>'<div class="nav-item'+(window._sc===it.id?' active':'')+'" onclick="_nav(\''+it.id+'\')">'+_ICONS[it.icon]+'<span>'+it.label+'</span></div>').join('');
|
||||
}
|
||||
|
||||
function _rs(){
|
||||
if(window._sc==='home') return _sHome();
|
||||
if(window._sc==='sales') return _sSales();
|
||||
if(window._sc==='finance') return _sFinance();
|
||||
if(window._sc==='team') return _sTeam();
|
||||
if(window._sc==='clients') return _sClients();
|
||||
if(window._sc==='sklad') return _sSklad();
|
||||
if(window._sc==='settings') return _sSettings();
|
||||
return _sHome();
|
||||
}
|
||||
|
||||
// ─── ГЛАВНАЯ ───────────────────────────────────────────────────────
|
||||
function _sHome(){
|
||||
var vis=window._salonVis;
|
||||
// Grouped bars: max по отдельным значениям каждого салона
|
||||
var allVals=_mdata.map(m=>[m.v*m.s1, m.v*(1-m.s1)]).reduce(function(a,b){return a.concat(b);},[]);
|
||||
var mx=Math.max.apply(null,allVals);
|
||||
var CHART_H=80; // px
|
||||
|
||||
// Toggle pills — клик включает/выключает салон
|
||||
var toggles='<div style="display:flex;gap:7px;padding:2px 16px 12px">'
|
||||
+'<div onclick="window._salonVis.s1=!window._salonVis.s1;window._monthDrill=null;_render()" style="cursor:pointer;display:inline-flex;align-items:center;gap:5px;padding:5px 12px;border-radius:20px;transition:.18s;background:'+(vis.s1?'#3B82F6':'rgba(0,0,0,.07)')+';border:1.5px solid '+(vis.s1?'#3B82F6':'rgba(0,0,0,.1)')+'"><span style="width:7px;height:7px;border-radius:50%;background:'+(vis.s1?'#fff':'#94A3B8')+';flex-shrink:0"></span><span style="font-size:11px;font-weight:700;color:'+(vis.s1?'#fff':'var(--muted)')+'">Салон Ленина</span></div>'
|
||||
+'<div onclick="window._salonVis.s2=!window._salonVis.s2;window._monthDrill=null;_render()" style="cursor:pointer;display:inline-flex;align-items:center;gap:5px;padding:5px 12px;border-radius:20px;transition:.18s;background:'+(vis.s2?'#8B5CF6':'rgba(0,0,0,.07)')+';border:1.5px solid '+(vis.s2?'#8B5CF6':'rgba(0,0,0,.1)')+'"><span style="width:7px;height:7px;border-radius:50%;background:'+(vis.s2?'#fff':'#94A3B8')+';flex-shrink:0"></span><span style="font-size:11px;font-weight:700;color:'+(vis.s2?'#fff':'var(--muted)')+'">Салон Победы</span></div>'
|
||||
+'</div>';
|
||||
|
||||
var bars=toggles
|
||||
+'<div style="display:flex;align-items:flex-end;gap:4px;height:'+CHART_H+'px;padding:0 16px">'
|
||||
+_mdata.map((m,i)=>{
|
||||
var sel=window._monthDrill===i;
|
||||
var hasOther=window._monthDrill!==null&&!sel;
|
||||
var op=sel?'1':hasOther?'0.2':(i===_mdata.length-1?'1':'0.7');
|
||||
var v1=m.v*m.s1; var v2=m.v*(1-m.s1);
|
||||
var h1=Math.round(v1/mx*CHART_H); var h2=Math.round(v2/mx*CHART_H);
|
||||
var selBorder=sel?'outline:2px solid var(--accent2);outline-offset:2px;':'';
|
||||
// Группа месяца
|
||||
return '<div style="flex:1;display:flex;align-items:flex-end;gap:2px;cursor:pointer;opacity:'+op+';transition:opacity .2s" onclick="window._monthDrill=(window._monthDrill==='+i+'?null:'+i+');window._drillSalon=\'all\';_render()">'
|
||||
// Бар Ленина
|
||||
+(vis.s1?'<div style="flex:1;height:'+h1+'px;background:#3B82F6;border-radius:3px 3px 0 0;'+selBorder+'"></div>':'<div style="flex:1"></div>')
|
||||
// Бар Победы
|
||||
+(vis.s2?'<div style="flex:1;height:'+h2+'px;background:#8B5CF6;border-radius:3px 3px 0 0;'+selBorder+'"></div>':'<div style="flex:1"></div>')
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
+'<div class="bar-labels">'+_mdata.map((m,i)=>{
|
||||
var sel=window._monthDrill===i;
|
||||
return '<div class="bar-label" style="cursor:pointer;'+(sel?'color:var(--accent);font-weight:800;':'')+'" onclick="window._monthDrill=(window._monthDrill==='+i+'?null:'+i+');window._drillSalon=\'all\';_render()">'
|
||||
+(sel?m.v.toFixed(2)+'М':m.m)+'</div>';
|
||||
}).join('')+'</div>';
|
||||
return '<div class="page">'
|
||||
+'<div class="hero-grad">'
|
||||
+ '<div style="font-size:11px;font-weight:700;opacity:.65;text-transform:uppercase;letter-spacing:.08em;margin-bottom:6px">МАЙ 2026</div>'
|
||||
+ '<div style="font-size:30px;font-weight:800;letter-spacing:-.04em">2 847 000 ₽</div>'
|
||||
+ '<div style="font-size:13px;opacity:.8;margin-top:5px">Выручка · <span style="color:#76BD22;font-weight:700">▲ +18%</span> к апрелю</div>'
|
||||
+ '<div style="display:flex;gap:20px;margin-top:16px;font-size:13px;opacity:.85">'
|
||||
+ '<div><div style="opacity:.65;font-size:11px">Прибыль</div><div style="font-weight:700">988 000 ₽</div></div>'
|
||||
+ '<div><div style="opacity:.65;font-size:11px">Маржа</div><div style="font-weight:700;color:#76BD22">34.7%</div></div>'
|
||||
+ '<div><div style="opacity:.65;font-size:11px">Заказов</div><div style="font-weight:700">47</div></div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">34.7%</div><div class="kpi-label">Маржа (EBITDA)</div><div class="kpi-delta up">▲ +2.1 пп к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">47</div><div class="kpi-label">Заказов за месяц</div><div class="kpi-delta up">▲ +6 к апрелю</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">51 800</div><div class="kpi-label">Средний чек, ₽</div><div class="kpi-delta up">▲ +3 200 ₽</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">4.8 / 5</div><div class="kpi-label">NPS клиентов</div><div class="kpi-delta up">▲ +0.1 пункт</div></div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="section-label" style="cursor:default">Выручка · последние 6 месяцев <span style="font-size:10px;color:var(--muted);font-weight:500;text-transform:none;letter-spacing:0">нажмите на бар</span></div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:16px 0 10px">'+bars+'</div></div>'
|
||||
+(window._monthDrill!==null ? '<div style="padding:0 16px">'+_drillPanel(window._monthDrill)+'</div>' : '')
|
||||
|
||||
// ── ПАЙПЛАЙН (одна строка-сигнал)
|
||||
+'<div style="padding:0 16px;margin-bottom:4px">'
|
||||
+'<div class="card" style="padding:13px 16px;cursor:pointer;display:flex;align-items:center;gap:12px" onclick="_nav(\'sales\')">'
|
||||
+ '<div style="flex:1">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">Пайплайн продаж · 47 заказов</div>'
|
||||
+ '<div style="display:flex;gap:12px;margin-top:5px">'
|
||||
+ '<span style="font-size:11px;color:var(--muted)">В работе: <b style="color:var(--ink)">2 847 000 ₽</b></span>'
|
||||
+ '<span style="font-size:11px;color:var(--danger);font-weight:700">⚠ 3 просрочки · 385 тыс риск</span>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '<span style="font-size:18px;color:var(--muted)">›</span>'
|
||||
+'</div></div>'
|
||||
|
||||
// ── ПРОБЛЕМЫ (кликабельные)
|
||||
+'<div class="section-label">🚨 Проблемы и срывы</div>'
|
||||
+'<div style="padding:0 16px">'
|
||||
+_problems().map(p=>{
|
||||
var exp = window._probExp===p.id;
|
||||
var border = p.sev==='red'?'var(--danger)':p.sev==='warn'?'var(--warn)':'var(--success)';
|
||||
var badgeCls = p.sev==='red'?'br':p.sev==='warn'?'bw':'bg';
|
||||
return '<div class="card" style="margin-bottom:8px;border-left:3px solid '+border+';cursor:pointer;padding:12px 14px" onclick="window._probExp=(window._probExp===\''+p.id+'\'?null:\''+p.id+'\');_render()">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:flex-start">'
|
||||
+ '<div style="flex:1">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">'+p.title+'</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:3px">'+p.sub+'</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="display:flex;align-items:center;gap:6px;flex-shrink:0;margin-left:8px">'
|
||||
+ '<span class="badge-sm '+badgeCls+'">'+p.badge+'</span>'
|
||||
+ '<span style="font-size:14px;color:var(--muted);transition:.2s;'+(exp?'transform:rotate(180deg)':'')+'">'+(exp?'▲':'▼')+'</span>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+(exp
|
||||
? '<div style="margin-top:10px;padding-top:10px;border-top:1px solid rgba(0,0,0,.07)">'
|
||||
+p.detail.map(d=>'<div style="font-size:12px;color:var(--ink);line-height:1.6;margin-bottom:4px">'+d+'</div>').join('')
|
||||
+(p.action?'<button style="margin-top:8px;padding:7px 14px;background:var(--accent);color:#fff;border:none;border-radius:8px;font-size:12px;font-weight:700;cursor:pointer">'+p.action+'</button>':'')
|
||||
+'</div>'
|
||||
: '')
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
function _problems(){
|
||||
return [
|
||||
{id:'p1', sev:'red', badge:'3', title:'3 заказа с просрочкой монтажа',
|
||||
sub:'Самый старый — 8 дней. Причина: Иван Т. заболел',
|
||||
detail:['🔴 КЧ-2841 · Просрочка 8 дн · Иван Т. (болеет)','🔴 КЧ-2798 · Просрочка 5 дн · не назначен исполнитель','🟡 ЗМ-1902 · Просрочка 3 дн · перенесён клиентом'],
|
||||
action:'Назначить исполнителей →'},
|
||||
{id:'p2', sev:'red', badge:'!', title:'Рекламация — ЗМ-1891',
|
||||
sub:'Клиент требует переделки фасадов · 3 дня без ответа',
|
||||
detail:['Клиент: Антонова Т.В.','Сборщик: Иван Т. (сейчас болеет)','Суть: перекос фасадов, зазоры > 3 мм','Статус: открыта 22.05, ответа нет'],
|
||||
action:'Открыть рекламацию →'},
|
||||
{id:'p3', sev:'warn', badge:'7', title:'Дебиторка: 287 000 ₽',
|
||||
sub:'7 клиентов · авансы > 14 дней',
|
||||
detail:['ООО «ДомСтрой» · 89 000 ₽ · 21 день','Козлов А. · 54 000 ₽ · 18 дней','Белова Н. · 38 000 ₽ · 16 дней','+ ещё 4 клиента на сумму 106 000 ₽'],
|
||||
action:'Поставить задачу менеджеру →'},
|
||||
{id:'p4', sev:'warn', badge:'79', title:'KPI Ольги Р. ниже нормы',
|
||||
sub:'Менеджер · KPI 79 · 2-й месяц подряд',
|
||||
detail:['Норма: ≥ 80','Апрель: 81 → Май: 79 (тренд вниз)','Причина: 3 потерянных лида на этапе КП','Рекомендация: встреча-разбор по скриптам'],
|
||||
action:'Поставить задачу директору →'},
|
||||
{id:'p5', sev:'warn', badge:'4', title:'Нет замерщика 28–30 мая',
|
||||
sub:'Марина С. в отпуске · 4 замера без исполнителя',
|
||||
detail:['28 мая: Иванов С. (Кировский р-н)','28 мая: ООО «МебельПлюс» (Центр)','29 мая: Петрова Н. (Советский р-н)','30 мая: Зайцев И. (Ленинский р-н)'],
|
||||
action:'Назначить Светлану М. →'},
|
||||
{id:'p6', sev:'ok', badge:'✅', title:'Кассовый разрыв — нет',
|
||||
sub:'Баланс в норме · Склад укомплектован',
|
||||
detail:['Остаток на счёте: 1 240 000 ₽','Ожидаемые поступления 7 дней: +680 000 ₽','Плановые выплаты 7 дней: −320 000 ₽','Склад: 97% позиций в наличии'],
|
||||
action:null},
|
||||
];
|
||||
}
|
||||
|
||||
// ─── ФИНАНСЫ ───────────────────────────────────────────────────────
|
||||
function _finTabBar(){
|
||||
return '<div style="display:flex;gap:0;padding:0 16px 0;margin-top:12px">'
|
||||
+['fact','forecast'].map((t,i)=>{
|
||||
var lbl = t==='fact'?'📊 Факт':'🔮 Прогноз';
|
||||
var act = window._finTab===t;
|
||||
return '<div onclick="window._finTab=\''+t+'\';_render()" style="flex:1;text-align:center;padding:9px 0;font-size:13px;font-weight:700;cursor:pointer;border-bottom:2px solid '+(act?'var(--accent)':'rgba(0,0,0,.1)')+';color:'+(act?'var(--accent)':'var(--muted)')+'">'+lbl+'</div>';
|
||||
}).join('')
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
function _sFinance(){
|
||||
var exp=[
|
||||
{label:'ФОТ', val:456000, pct:70, color:'#3B82F6'},
|
||||
{label:'Логистика', val:89000, pct:14, color:'#8B5CF6'},
|
||||
{label:'Аренда', val:48000, pct:7, color:'#F59E0B'},
|
||||
{label:'Реклама', val:78000, pct:12, color:'#10B981'},
|
||||
{label:'Прочие', val:58000, pct:9, color:'#EF4444'},
|
||||
];
|
||||
var hist=[
|
||||
{m:'Фев',rev:2.1, pr:672},
|
||||
{m:'Мар',rev:1.9, pr:589},
|
||||
{m:'Апр',rev:2.4, pr:792},
|
||||
{m:'Май',rev:2.85,pr:988,cur:true},
|
||||
];
|
||||
var header = '<div style="padding:20px 16px 0;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+'<div style="font-size:17px;font-weight:800;color:var(--ink)">Финансы</div>'
|
||||
+'<div style="font-size:12px;color:var(--muted);margin-top:2px">Май 2026</div>'
|
||||
+_finTabBar()
|
||||
+'</div>';
|
||||
|
||||
if(window._finTab==='forecast') return _sFinanceForecast(header);
|
||||
|
||||
// ── ФАКТ ──
|
||||
return '<div class="page">'+header
|
||||
+'<div style="padding:16px 16px 0">'
|
||||
+'<div class="card" style="background:linear-gradient(135deg,var(--accent),#005BB5);color:#fff;padding:20px 20px 16px">'
|
||||
+ '<div style="font-size:11px;opacity:.7;font-weight:700;text-transform:uppercase;letter-spacing:.08em">Чистая прибыль · Май</div>'
|
||||
+ '<div style="font-size:34px;font-weight:800;letter-spacing:-.04em;margin-top:4px">988 000 ₽</div>'
|
||||
+ '<div style="display:flex;gap:20px;margin-top:12px;font-size:12px;opacity:.85">'
|
||||
+ '<div><div style="opacity:.65">Выручка</div><div style="font-weight:700">2 847 000 ₽</div></div>'
|
||||
+ '<div><div style="opacity:.65">Расходы</div><div style="font-weight:700">1 859 000 ₽</div></div>'
|
||||
+ '<div><div style="opacity:.65">Маржа</div><div style="font-weight:700;color:#76BD22">34.7%</div></div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="section-label">Структура расходов</div>'
|
||||
+'<div style="padding:0 16px"><div class="card">'
|
||||
+exp.map(e=>'<div style="margin-bottom:12px">'
|
||||
+'<div style="display:flex;justify-content:space-between;margin-bottom:5px">'
|
||||
+ '<span style="font-size:13px;color:var(--ink);font-weight:500">'+e.label+'</span>'
|
||||
+ '<span style="font-size:13px;font-weight:700;color:var(--ink)">'+(e.val/1000).toFixed(0)+' тыс ₽</span>'
|
||||
+'</div>'
|
||||
+'<div class="prog-bg" style="margin:0"><div style="height:100%;border-radius:4px;background:'+e.color+';width:'+e.pct+'%"></div></div>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
|
||||
+'<div class="section-label">Динамика по месяцам</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+hist.map(h=>'<div style="display:flex;align-items:center;padding:12px 16px;border-bottom:1px solid rgba(0,0,0,.05);'+(h.cur?'background:var(--s-info-bg)':'')+'">'
|
||||
+'<div style="font-size:13px;font-weight:700;color:var(--ink);width:34px">'+h.m+'</div>'
|
||||
+'<div style="flex:1;padding-left:10px">'
|
||||
+ '<div style="font-size:11px;color:var(--muted)">Выручка</div>'
|
||||
+ '<div style="font-size:14px;font-weight:700;color:var(--ink)">'+h.rev.toFixed(2)+' М ₽</div>'
|
||||
+'</div>'
|
||||
+'<div style="text-align:right">'
|
||||
+ '<div style="font-size:11px;color:var(--muted)">Прибыль</div>'
|
||||
+ '<div style="font-size:14px;font-weight:700;color:var(--success)">'+h.pr+' тыс</div>'
|
||||
+'</div>'
|
||||
+(h.cur?'<div style="margin-left:8px"><span class="badge-sm bb">тек.</span></div>':'')
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
function _sFinanceForecast(header){
|
||||
var cf=[
|
||||
{label:'Остаток на счёте', val:'+1 240 000', color:'var(--success)', note:'сегодня'},
|
||||
{label:'Ожидаемые поступления', val:'+1 680 000', color:'var(--success)', note:'7 дн · из воронки'},
|
||||
{label:'ФОТ (аванс 1 июня)', val:'−228 000', color:'var(--danger)', note:'через 5 дней'},
|
||||
{label:'Подрядчики / доставка',val:'−147 000', color:'var(--danger)', note:'7 дн · 3 счёта'},
|
||||
{label:'Аренда + хоз.', val:'−48 000', color:'var(--danger)', note:'1 июня'},
|
||||
];
|
||||
var pipe=[
|
||||
{stage:'Договор → Монтаж', cnt:8, sum:'684 000', prob:90, color:'#10B981'},
|
||||
{stage:'КП отправлено', cnt:11,sum:'912 000', prob:55, color:'#3B82F6'},
|
||||
{stage:'На замере', cnt:7, sum:'490 000', prob:35, color:'#F59E0B'},
|
||||
{stage:'Новые лиды', cnt:14,sum:'1 120 000',prob:15,color:'#8B5CF6'},
|
||||
];
|
||||
var expected = 684*0.9 + 912*0.55 + 490*0.35 + 1120*0.15;
|
||||
return '<div class="page">'+header
|
||||
+'<div style="padding:16px 16px 0">'
|
||||
+'<div class="card" style="background:linear-gradient(135deg,#065F46,#10B981);color:#fff;padding:20px 20px 16px">'
|
||||
+ '<div style="font-size:11px;opacity:.7;font-weight:700;text-transform:uppercase;letter-spacing:.08em">Прогноз · следующие 30 дней</div>'
|
||||
+ '<div style="font-size:34px;font-weight:800;letter-spacing:-.04em;margin-top:4px">+2 497 000 ₽</div>'
|
||||
+ '<div style="font-size:13px;opacity:.85;margin-top:6px">Ожидаемый баланс с учётом поступлений и выплат</div>'
|
||||
+ '<div style="display:flex;gap:20px;margin-top:12px;font-size:12px;opacity:.85">'
|
||||
+ '<div><div style="opacity:.65">Взвешенный пайплайн</div><div style="font-weight:700">'+Math.round(expected)+' тыс ₽</div></div>'
|
||||
+ '<div><div style="opacity:.65">Выплаты</div><div style="font-weight:700">−423 тыс ₽</div></div>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="section-label">Cash flow · 30 дней</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+cf.map(r=>'<div style="display:flex;align-items:center;padding:12px 16px;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="flex:1"><div style="font-size:13px;color:var(--ink);font-weight:500">'+r.label+'</div><div style="font-size:11px;color:var(--muted);margin-top:1px">'+r.note+'</div></div>'
|
||||
+'<div style="font-size:14px;font-weight:800;color:'+r.color+'">'+r.val+' ₽</div>'
|
||||
+'</div>').join('')
|
||||
+'<div style="padding:12px 16px;border-top:2px solid rgba(0,0,0,.08);display:flex;justify-content:space-between">'
|
||||
+ '<span style="font-size:13px;font-weight:700;color:var(--ink)">Итого через 30 дней</span>'
|
||||
+ '<span style="font-size:14px;font-weight:800;color:var(--success)">+2 497 000 ₽</span>'
|
||||
+'</div>'
|
||||
+'</div></div>'
|
||||
|
||||
+'<div class="section-label">Взвешенный пайплайн</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+pipe.map(p=>'<div style="padding:12px 16px;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">'
|
||||
+ '<div><div style="font-size:13px;font-weight:600;color:var(--ink)">'+p.stage+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted)">'+p.cnt+' заказов · вероятность '+p.prob+'%</div></div>'
|
||||
+ '<div style="text-align:right"><div style="font-size:13px;font-weight:700;color:var(--ink)">'+p.sum+' ₽</div>'
|
||||
+ '<div style="font-size:11px;color:'+p.color+';font-weight:700">≈'+(parseInt(p.sum.replace(/\s/g,''))/1000*p.prob/100).toFixed(0)+' тыс</div></div>'
|
||||
+'</div>'
|
||||
+'<div class="prog-bg" style="margin:0"><div style="height:100%;border-radius:4px;background:'+p.color+';width:'+p.prob+'%"></div></div>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── ПРОДАЖИ ───────────────────────────────────────────────────────
|
||||
function _sSales(){
|
||||
var stages=[
|
||||
{id:'new', label:'Новые лиды', cnt:14, sum:'1 120 000', color:'#8B5CF6', pct:100},
|
||||
{id:'meet', label:'На замере', cnt:7, sum:'490 000', color:'#3B82F6', pct:50},
|
||||
{id:'kp', label:'КП отправлено',cnt:11,sum:'912 000', color:'#F59E0B', pct:79},
|
||||
{id:'think', label:'Думает', cnt:5, sum:'380 000', color:'#F97316', pct:36},
|
||||
{id:'deal', label:'Договор', cnt:8, sum:'684 000', color:'#10B981', pct:57},
|
||||
{id:'mount', label:'Монтаж', cnt:6, sum:'512 000', color:'#059669', pct:43},
|
||||
{id:'lost', label:'Потерян', cnt:4, sum:'290 000', color:'#EF4444', pct:29},
|
||||
];
|
||||
var conv=[
|
||||
{from:'Лид → Замер', pct:58, good:true},
|
||||
{from:'Замер → КП', pct:94, good:true},
|
||||
{from:'КП → Договор', pct:48, good:false},
|
||||
{from:'Договор → Монтаж',pct:100,good:true},
|
||||
];
|
||||
var orders=[
|
||||
{id:'КЧ-2847', client:'ООО «РемСтрой»', sum:'187 000', stage:'Монтаж', days:2, mgr:'Дмитрий К.', salon:'Ленина', warn:false},
|
||||
{id:'ЗМ-2831', client:'Надежда Орлова', sum:'142 000', stage:'Договор', days:5, mgr:'Дмитрий К.', salon:'Победы', warn:false},
|
||||
{id:'КЧ-2819', client:'ИП Сидоров', sum:'128 500', stage:'КП', days:1, mgr:'Ольга Р.', salon:'Ленина', warn:false},
|
||||
{id:'ЗМ-2841', client:'Анна Белова', sum:'113 000', stage:'Монтаж', days:0, mgr:'Ольга Р.', salon:'Победы', warn:true},
|
||||
{id:'КЧ-2798', client:'Сергей Павлов', sum:'98 500', stage:'Монтаж', days:-5, mgr:'Дмитрий К.', salon:'Ленина', warn:true},
|
||||
{id:'КЧ-2789', client:'Тамара Н.', sum:'94 000', stage:'Думает', days:8, mgr:'Ольга Р.', salon:'Ленина', warn:false},
|
||||
{id:'ЗМ-2774', client:'ООО «МебельПлюс»', sum:'88 000', stage:'КП', days:3, mgr:'Дмитрий К.', salon:'Победы', warn:false},
|
||||
];
|
||||
var stageColor={Монтаж:'#10B981',Договор:'#3B82F6',КП:'#F59E0B',Думает:'#F97316','Новый':'#8B5CF6'};
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Продажи</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">Май 2026 · активная воронка</div>'
|
||||
+'</div>'
|
||||
|
||||
// KPI strip
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">47</div><div class="kpi-label">Заказов в работе</div><div class="kpi-delta up">▲ +6 к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">3.6 М</div><div class="kpi-label">Сумма воронки, ₽</div><div class="kpi-delta up">▲ +14%</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">48%</div><div class="kpi-label">Конверсия КП→Дог.</div><div class="kpi-delta dn">▼ −5 пп</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">21 дн</div><div class="kpi-label">Ср. цикл сделки</div><div class="kpi-delta up">▼ −2 дня</div></div>'
|
||||
+'</div>'
|
||||
|
||||
// Воронка
|
||||
+'<div class="section-label">Воронка продаж</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:12px 16px">'
|
||||
+stages.map(s=>'<div style="margin-bottom:10px">'
|
||||
+'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px">'
|
||||
+ '<div style="display:flex;align-items:center;gap:8px">'
|
||||
+ '<span style="width:8px;height:8px;border-radius:50%;background:'+s.color+';display:inline-block;flex-shrink:0"></span>'
|
||||
+ '<span style="font-size:13px;color:var(--ink);font-weight:500">'+s.label+'</span>'
|
||||
+ '<span class="badge-sm" style="background:'+s.color+'18;color:'+s.color+'">'+s.cnt+'</span>'
|
||||
+ '</div>'
|
||||
+ '<span style="font-size:12px;font-weight:700;color:var(--ink)">'+s.sum+' ₽</span>'
|
||||
+'</div>'
|
||||
+'<div class="prog-bg" style="margin:0"><div style="height:100%;border-radius:4px;background:'+s.color+';width:'+s.pct+'%"></div></div>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
|
||||
// Конверсия
|
||||
+'<div class="section-label">Конверсия по этапам</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+conv.map(c=>'<div style="display:flex;align-items:center;padding:11px 16px;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="flex:1;font-size:13px;color:var(--ink)">'+c.from+'</div>'
|
||||
+'<div style="font-size:16px;font-weight:800;color:'+(c.good?'var(--success)':'var(--danger)')+'">'+c.pct+'%</div>'
|
||||
+'</div>').join('')
|
||||
+'<div style="padding:10px 16px;background:var(--s-warning-bg);border-radius:0 0 12px 12px">'
|
||||
+ '<div style="font-size:12px;color:#92400E;font-weight:600">⚠️ КП→Договор 48% — ниже нормы 55%. Разобрать с менеджерами скрипты КП.</div>'
|
||||
+'</div>'
|
||||
+'</div></div>'
|
||||
|
||||
// Список заказов
|
||||
+'<div class="section-label">Все заказы в работе</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+orders.map(o=>'<div style="display:flex;align-items:center;padding:11px 16px;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="flex:1;min-width:0">'
|
||||
+ '<div style="display:flex;align-items:center;gap:6px">'
|
||||
+ '<span style="font-size:12px;font-weight:700;color:var(--accent)">'+o.id+'</span>'
|
||||
+ (o.warn?'<span style="font-size:10px;font-weight:700;color:var(--danger)">⚠ просрочка</span>':'')
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:12px;color:var(--ink);font-weight:500;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+o.client+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">Салон '+o.salon+' · '+o.mgr+'</div>'
|
||||
+'</div>'
|
||||
+'<div style="text-align:right;flex-shrink:0;margin-left:8px">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">'+o.sum+' ₽</div>'
|
||||
+ '<div style="display:inline-flex;margin-top:3px;background:'+(stageColor[o.stage]||'var(--accent)')+'18;border-radius:6px;padding:2px 7px">'
|
||||
+ '<span style="font-size:10px;font-weight:700;color:'+(stageColor[o.stage]||'var(--accent)')+'">'+o.stage+'</span>'
|
||||
+ '</div>'
|
||||
+'</div>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── КОМАНДА (executive view для ГД) ──────────────────────────────
|
||||
function _sTeam(){
|
||||
// Данные по салонам
|
||||
var salons=[
|
||||
{name:'Салон Ленина', color:'#3B82F6', bg:'#EFF6FF',
|
||||
staff:4, rev:'1 537 000', revMgr:'768 500', orders:27, conv:54, kpiAvg:88,
|
||||
flags:1, flagDesc:'Ольга Р. · KPI 79 · 2-й мес'},
|
||||
{name:'Салон Победы', color:'#8B5CF6', bg:'#F5F3FF',
|
||||
staff:4, rev:'1 310 000', revMgr:'655 000', orders:20, conv:48, kpiAvg:81,
|
||||
flags:2, flagDesc:'Иван Т. болеет · просрочки'},
|
||||
];
|
||||
// Роли агрегированно
|
||||
var roles=[
|
||||
{role:'Менеджеры', icon:'📋', count:2, kpi:87, norm:80, flags:1, flagDesc:'Ольга Р. ниже нормы 2 мес'},
|
||||
{role:'Сборщики', icon:'🔧', count:3, kpi:83, norm:80, flags:1, flagDesc:'Иван Т. болеет · 3 просрочки'},
|
||||
{role:'Замерщики', icon:'📐', count:2, kpi:89, norm:80, flags:0, flagDesc:''},
|
||||
{role:'Руководство', icon:'🏢', count:1, kpi:88, norm:80, flags:1, flagDesc:'Не перераспределил заказы'},
|
||||
];
|
||||
// Критические флаги — только для ГД
|
||||
var critFlags=[
|
||||
{sev:'danger', text:'Иван Т. болеет 3-й день', sub:'3 просрочки монтажа · рекламация ЗМ-1891 без ответа'},
|
||||
{sev:'warn', text:'Ольга Р. · KPI 79 · 2-й месяц подряд', sub:'Салон Победы · план/факт: 12/15'},
|
||||
{sev:'warn', text:'Марина С. в отпуске 28–30 мая', sub:'4 замера без замены · риск переноса'},
|
||||
];
|
||||
var sc={danger:{c:'var(--danger)',bg:'#FEF2F2',ic:'🔴'},warn:{c:'var(--warn)',bg:'#FFFBEB',ic:'🟡'}};
|
||||
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Команда</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">8 сотрудников · ФОТ 456 000 ₽/мес</div>'
|
||||
+'</div>'
|
||||
|
||||
// KPI сводка
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">16%</div><div class="kpi-label">ФОТ / выручка</div><div class="kpi-delta up">▼ −1.2 пп к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">85</div><div class="kpi-label">Средний KPI</div><div class="kpi-delta up">▲ +2 к апр.</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value" style="color:var(--danger)">3</div><div class="kpi-label">Требуют внимания</div><div class="kpi-delta dn">из 8 сотрудников</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">8 / 8</div><div class="kpi-label">Укомплектованность</div><div class="kpi-delta up">Вакансий нет</div></div>'
|
||||
+'</div>'
|
||||
|
||||
// Сравнение салонов
|
||||
+'<div class="section-label">Эффективность по салонам</div>'
|
||||
+'<div style="padding:0 16px;display:grid;grid-template-columns:1fr 1fr;gap:10px">'
|
||||
+salons.map(s=>`
|
||||
<div class="card" style="padding:14px;border-top:3px solid ${s.color}">
|
||||
<div style="font-size:12px;font-weight:800;color:${s.color};margin-bottom:8px">${s.name}</div>
|
||||
<div style="font-size:11px;color:var(--muted);margin-bottom:2px">Выручка/мес</div>
|
||||
<div style="font-size:15px;font-weight:800;color:var(--ink);margin-bottom:6px">${s.rev} ₽</div>
|
||||
<div class="stat-row" style="padding:4px 0"><span class="stat-l" style="font-size:11px">Выр./менеджер</span><span class="stat-v" style="font-size:11px">${s.revMgr} ₽</span></div>
|
||||
<div class="stat-row" style="padding:4px 0"><span class="stat-l" style="font-size:11px">Конверсия</span><span class="stat-v ${s.conv>=50?'g':'r'}" style="font-size:11px">${s.conv}%</span></div>
|
||||
<div class="stat-row" style="padding:4px 0;border:none"><span class="stat-l" style="font-size:11px">Ср. KPI</span><span class="stat-v ${s.kpiAvg>=85?'g':'r'}" style="font-size:11px">${s.kpiAvg}</span></div>
|
||||
${s.flags>0?`<div style="margin-top:6px;padding:5px 8px;background:#FEF2F2;border-radius:8px;font-size:10px;color:var(--danger);font-weight:600">⚠ ${s.flagDesc}</div>`:'<div style="margin-top:6px;padding:5px 8px;background:#ECFDF5;border-radius:8px;font-size:10px;color:var(--success);font-weight:600">✅ Всё в порядке</div>'}
|
||||
</div>`).join('')
|
||||
+'</div>'
|
||||
|
||||
// Команда по ролям
|
||||
+'<div class="section-label">Команда по ролям</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+roles.map((r,i)=>{
|
||||
var kpiColor=r.kpi>=r.norm?'var(--success)':'var(--danger)';
|
||||
return '<div style="display:flex;align-items:center;padding:12px 14px;border-bottom:'+(i<roles.length-1?'1px solid rgba(0,0,0,.05)':'none')+'">'
|
||||
+'<div style="font-size:20px;margin-right:10px;line-height:1">'+r.icon+'</div>'
|
||||
+'<div style="flex:1">'
|
||||
+ '<div style="font-size:13px;font-weight:700;color:var(--ink)">'+r.role+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">'+r.count+' чел</div>'
|
||||
+'</div>'
|
||||
+'<div style="text-align:right;margin-right:10px">'
|
||||
+ '<div style="font-size:15px;font-weight:800;color:'+kpiColor+'">'+r.kpi+'</div>'
|
||||
+ '<div style="font-size:9px;color:var(--muted);text-transform:uppercase">KPI</div>'
|
||||
+'</div>'
|
||||
+(r.flags>0
|
||||
? '<div style="background:#FEF2F288;border-radius:8px;padding:4px 8px;max-width:110px;text-align:right"><div style="font-size:10px;color:var(--danger);font-weight:600;line-height:1.3">'+r.flagDesc+'</div></div>'
|
||||
: '<div style="background:#ECFDF588;border-radius:8px;padding:4px 8px"><div style="font-size:10px;color:var(--success);font-weight:600">Всё ок</div></div>')
|
||||
+'</div>';
|
||||
}).join('')
|
||||
+'</div></div>'
|
||||
|
||||
// Критические флаги
|
||||
+'<div class="section-label">Требует решения</div>'
|
||||
+'<div style="padding:0 16px">'
|
||||
+critFlags.map(f=>`
|
||||
<div style="display:flex;align-items:flex-start;gap:10px;background:${sc[f.sev].bg};border-radius:14px;padding:12px 14px;margin-bottom:8px">
|
||||
<span style="font-size:16px;flex-shrink:0;margin-top:1px">${sc[f.sev].ic}</span>
|
||||
<div style="flex:1">
|
||||
<div style="font-size:13px;font-weight:600;color:var(--ink);line-height:1.35">${f.text}</div>
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:3px">${f.sub}</div>
|
||||
</div>
|
||||
</div>`).join('')
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── КЛИЕНТЫ ───────────────────────────────────────────────────────
|
||||
function _sClients(){
|
||||
var top=[
|
||||
{name:'ООО «РемСтрой»', orders:8, ltv:'587 000', em:'🏢'},
|
||||
{name:'Надежда Орлова', orders:5, ltv:'312 000', em:'👩'},
|
||||
{name:'Анна Белова', orders:4, ltv:'243 000', em:'👩'},
|
||||
{name:'Сергей Павлов', orders:3, ltv:'178 000', em:'👨'},
|
||||
];
|
||||
var src=[['Рекомендации',42,'#10B981'],['Авито',28,'#3B82F6'],['Instagram',18,'#8B5CF6'],['Сайт / SEO',12,'#F59E0B']];
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Клиенты</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">Май 2026</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="kpi-grid">'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">312</div><div class="kpi-label">Всего клиентов</div><div class="kpi-delta up">▲ +23 за месяц</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">4.8 / 5</div><div class="kpi-label">Средний NPS</div><div class="kpi-delta up">▲ +0.1 пункт</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">89 000</div><div class="kpi-label">Средний LTV, ₽</div><div class="kpi-delta up">▲ +4 200</div></div>'
|
||||
+ '<div class="kpi-card"><div class="kpi-value">1.8%</div><div class="kpi-label">Отток</div><div class="kpi-delta up">▼ −0.4 пп</div></div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="section-label">Источники привлечения</div>'
|
||||
+'<div style="padding:0 16px"><div class="card">'
|
||||
+src.map(s=>'<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px">'
|
||||
+'<div style="font-size:12px;color:var(--muted);font-weight:500;width:110px">'+s[0]+'</div>'
|
||||
+'<div class="prog-bg" style="margin:0"><div style="height:100%;border-radius:4px;background:'+s[2]+';width:'+s[1]+'%"></div></div>'
|
||||
+'<div style="font-size:13px;font-weight:700;color:var(--ink);width:32px;text-align:right">'+s[1]+'%</div>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
|
||||
+'<div class="section-label">Топ по LTV</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:8px 16px">'
|
||||
+top.map(c=>'<div class="row-item">'
|
||||
+'<div class="avatar-sm" style="background:var(--s-info-bg);font-size:19px">'+c.em+'</div>'
|
||||
+'<div style="flex:1"><div class="ri-name">'+c.name+'</div><div class="ri-sub">'+c.orders+' заказов</div></div>'
|
||||
+'<div style="text-align:right"><div style="font-size:13px;font-weight:700;color:var(--ink)">'+c.ltv+' ₽</div><div style="font-size:10px;color:var(--muted)">LTV</div></div>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
// ─── СКЛАД И ДОСТАВКА (executive view для ГД) ──────────────────────
|
||||
function _sSklad(){
|
||||
// Факторы риска от фабрик
|
||||
var factories=[
|
||||
{name:'МебельМастер', orders:14, delayed:2, delayDays:3, sum:'184 000'},
|
||||
{name:'КорпусПро', orders:9, delayed:0, delayDays:0, sum:'97 000'},
|
||||
{name:'СтильДом', orders:5, delayed:1, delayDays:6, sum:'63 000'},
|
||||
];
|
||||
// Критические ситуации — только для ГД
|
||||
var alerts=[
|
||||
{type:'danger', text:'СтильДом задерживает ЗАК-1839 на 6 дней — монтаж под угрозой', sub:'Клиент Иванова Т. · 63 000 ₽'},
|
||||
{type:'warn', text:'МебельМастер: 2 заказа без подтверждения даты отгрузки', sub:'ЗАК-1855, ЗАК-1862 · до 30 мая'},
|
||||
{type:'ok', text:'Доставки сегодня: 3/5 выполнено, 1 просрочка на 2ч', sub:'ДОС-288 · Сергей Павлов · Алексей Г.'},
|
||||
];
|
||||
var ac={danger:{c:'var(--danger)',bg:'#FEF2F2',ic:'🔴'},warn:{c:'var(--warn)',bg:'#FFFBEB',ic:'🟡'},ok:{c:'var(--success)',bg:'#ECFDF5',ic:'🟢'}};
|
||||
|
||||
return `<div class="page">
|
||||
<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">
|
||||
<div style="font-size:17px;font-weight:800;color:var(--ink)">Склад и доставка</div>
|
||||
<div style="font-size:12px;color:var(--muted);margin-top:2px">Май 2026</div>
|
||||
</div>
|
||||
|
||||
<!-- KPI сводка -->
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-value">78%</div>
|
||||
<div class="kpi-label">В срок за май</div>
|
||||
<div class="kpi-delta dn">▼ план 90%</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-value" style="color:var(--danger)">3</div>
|
||||
<div class="kpi-label">Задержки фабрик</div>
|
||||
<div class="kpi-delta dn">риск 147 тыс ₽</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-value">28</div>
|
||||
<div class="kpi-label">Заказов в работе</div>
|
||||
<div class="kpi-delta up">↑ 4 vs прош. мес</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-value">4.7</div>
|
||||
<div class="kpi-label">Рейтинг доставки</div>
|
||||
<div class="kpi-delta up">▲ 4.5 прош. мес</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Требует внимания -->
|
||||
<div class="section-label">Требует внимания</div>
|
||||
<div style="padding:0 16px">
|
||||
${alerts.map(a=>`
|
||||
<div style="display:flex;align-items:flex-start;gap:10px;background:${ac[a.type].bg};border-radius:14px;padding:12px 14px;margin-bottom:8px">
|
||||
<span style="font-size:16px;flex-shrink:0;margin-top:1px">${ac[a.type].ic}</span>
|
||||
<div style="flex:1;min-width:0">
|
||||
<div style="font-size:13px;font-weight:600;color:var(--ink);line-height:1.35">${a.text}</div>
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:3px">${a.sub}</div>
|
||||
</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
|
||||
<!-- Фабрики-поставщики -->
|
||||
<div class="section-label">Фабрики-поставщики</div>
|
||||
<div style="padding:0 16px">
|
||||
<div class="card" style="padding:0">
|
||||
${factories.map((f,i)=>`
|
||||
<div style="padding:13px 14px;border-bottom:${i<factories.length-1?'1px solid rgba(0,0,0,.05)':'none'}">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start">
|
||||
<div>
|
||||
<div style="font-size:14px;font-weight:700;color:var(--ink)">${f.name}</div>
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:2px">${f.orders} заказов · ${f.sum} ₽</div>
|
||||
</div>
|
||||
${f.delayed>0
|
||||
? `<div style="background:#FEF2F2;border-radius:10px;padding:4px 10px;text-align:right">
|
||||
<div style="font-size:13px;font-weight:800;color:var(--danger)">${f.delayed} задерж.</div>
|
||||
<div style="font-size:10px;color:#991B1B">+${f.delayDays} дн avg</div>
|
||||
</div>`
|
||||
: `<div style="background:#ECFDF5;border-radius:10px;padding:4px 10px">
|
||||
<div style="font-size:12px;font-weight:700;color:var(--success)">В норме</div>
|
||||
</div>`
|
||||
}
|
||||
</div>
|
||||
<!-- Прогресс-бар исполнения -->
|
||||
<div style="margin-top:8px">
|
||||
<div style="display:flex;justify-content:space-between;margin-bottom:3px">
|
||||
<span style="font-size:10px;color:var(--muted)">Исполнение в срок</span>
|
||||
<span style="font-size:10px;font-weight:700;color:${f.delayed>0?'var(--warn)':'var(--success)'}">${Math.round((f.orders-f.delayed)/f.orders*100)}%</span>
|
||||
</div>
|
||||
<div style="height:4px;background:rgba(0,0,0,.07);border-radius:2px">
|
||||
<div style="height:100%;background:${f.delayed>0?'var(--warn)':'var(--success)'};border-radius:2px;width:${Math.round((f.orders-f.delayed)/f.orders*100)}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Доставки сегодня (только сводка) -->
|
||||
<div class="section-label">Доставки сегодня</div>
|
||||
<div style="padding:0 16px">
|
||||
<div class="card" style="padding:14px 16px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
|
||||
<span style="font-size:14px;font-weight:700;color:var(--ink)">5 доставок</span>
|
||||
<span style="font-size:12px;color:var(--muted)">2 экспедитора</span>
|
||||
</div>
|
||||
<div style="display:flex;gap:0;height:8px;border-radius:6px;overflow:hidden">
|
||||
<div style="flex:3;background:#10B981" title="3 доставлено"></div>
|
||||
<div style="flex:1;background:#3B82F6;margin-left:2px" title="1 в пути"></div>
|
||||
<div style="flex:1;background:#94A3B8;margin-left:2px" title="1 запланировано"></div>
|
||||
<div style="flex:1;background:#EF4444;margin-left:2px" title="1 просрочка"></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:12px;margin-top:8px">
|
||||
${[{c:'#10B981',l:'3 выполнено'},{c:'#3B82F6',l:'1 в пути'},{c:'#94A3B8',l:'1 план.'},{c:'#EF4444',l:'1 просрочка'}]
|
||||
.map(s=>`<span style="display:flex;align-items:center;gap:4px;font-size:10px;color:var(--muted)"><span style="width:6px;height:6px;border-radius:50%;background:${s.c};display:inline-block"></span>${s.l}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ─── НАСТРОЙКИ ─────────────────────────────────────────────────────
|
||||
function _sSettings(){
|
||||
var roles=[
|
||||
['👤','Собственник','Полный доступ, все отчёты','1'],
|
||||
['🏢','Директор', 'Все модули, кроме биллинга','1'],
|
||||
['📋','Менеджер', 'Заказы, клиенты, KB','2'],
|
||||
['🔧','Сборщик', 'Свои сборки, замеры','3'],
|
||||
['📐','Замерщик', 'Объекты, замеры','2'],
|
||||
['👤','Клиент', 'Статус заказа, акты','—'],
|
||||
];
|
||||
var notifs=[
|
||||
['Просрочки монтажа','Вкл','g'],
|
||||
['Крупные заказы > 100 тыс','Вкл','g'],
|
||||
['Дебиторка > 30 дней','Вкл','g'],
|
||||
['Еженедельный P&L','Пн 09:00','b'],
|
||||
['Новый крупный клиент','Вкл','g'],
|
||||
];
|
||||
return '<div class="page">'
|
||||
+'<div style="padding:20px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
|
||||
+ '<div style="font-size:17px;font-weight:800;color:var(--ink)">Настройки</div>'
|
||||
+'</div>'
|
||||
|
||||
+'<div class="section-label">Тарифный план</div>'
|
||||
+'<div style="padding:0 16px"><div class="card ok-b">'
|
||||
+ '<div style="display:flex;justify-content:space-between;align-items:center">'
|
||||
+ '<div>'
|
||||
+ '<div style="font-size:15px;font-weight:700;color:var(--ink)">Тариф «Профи»</div>'
|
||||
+ '<div style="font-size:12px;color:var(--muted);margin-top:2px">До 20 сотрудников · Все модули</div>'
|
||||
+ '</div>'
|
||||
+ '<span class="badge-sm bg">Активен</span>'
|
||||
+ '</div>'
|
||||
+ '<div class="stat-row" style="margin-top:12px"><span class="stat-l">Следующий платёж</span><span class="stat-v">01.06.2026</span></div>'
|
||||
+ '<div class="stat-row"><span class="stat-l">Сумма</span><span class="stat-v b">4 900 ₽/мес</span></div>'
|
||||
+ '<div class="stat-row"><span class="stat-l">Сотрудников</span><span class="stat-v">8 / 20</span></div>'
|
||||
+'</div></div>'
|
||||
|
||||
+'<div class="section-label">Роли и доступ</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+roles.map(r=>'<div style="display:flex;align-items:center;padding:11px 16px;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="font-size:18px;margin-right:10px">'+r[0]+'</div>'
|
||||
+'<div style="flex:1">'
|
||||
+ '<div style="font-size:13px;font-weight:600;color:var(--ink)">'+r[1]+'</div>'
|
||||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">'+r[2]+'</div>'
|
||||
+'</div>'
|
||||
+'<span class="badge-sm" style="background:var(--bg);color:var(--muted);border:1px solid rgba(0,0,0,.09)">'+r[3]+'</span>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
|
||||
+'<div class="section-label">Уведомления</div>'
|
||||
+'<div style="padding:0 16px"><div class="card" style="padding:0">'
|
||||
+notifs.map(n=>'<div style="display:flex;align-items:center;padding:11px 16px;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||||
+'<div style="flex:1;font-size:13px;color:var(--ink)">'+n[0]+'</div>'
|
||||
+'<span class="badge-sm '+(n[2]==='g'?'bg':'bb')+'">'+n[1]+'</span>'
|
||||
+'</div>').join('')
|
||||
+'</div></div>'
|
||||
+'</div>';
|
||||
}
|
||||
|
||||
_render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
618
Mokap/presentation_architecture.html
Normal file
618
Mokap/presentation_architecture.html
Normal file
@ -0,0 +1,618 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{
|
||||
--bg:#0F0F1A;
|
||||
--accent:#3B82F6;
|
||||
--accent2:#60A5FA;
|
||||
--success:#10B981;
|
||||
--warn:#F59E0B;
|
||||
--text:#F8FAFC;
|
||||
--card:#1A1A2E;
|
||||
--muted:#94A3B8;
|
||||
--border:rgba(148,163,184,.16);
|
||||
--mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||
}
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
html,body{height:100%}
|
||||
body{
|
||||
font-family:'Inter',system-ui,sans-serif;
|
||||
background:var(--bg);
|
||||
color:var(--text);
|
||||
overflow:hidden;
|
||||
}
|
||||
/* ===== DECK ===== */
|
||||
.deck{position:fixed;inset:0}
|
||||
.slide{
|
||||
position:absolute;inset:0;
|
||||
display:flex;flex-direction:column;
|
||||
padding:64px 88px 90px;
|
||||
opacity:0;visibility:hidden;
|
||||
transform:translateY(14px);
|
||||
transition:opacity .5s ease,transform .5s ease,visibility .5s;
|
||||
overflow:hidden;
|
||||
}
|
||||
.slide.active{opacity:1;visibility:visible;transform:none;z-index:2}
|
||||
/* aspect frame to keep 16:9 readable on any screen */
|
||||
.stage{
|
||||
position:relative;width:100%;height:100%;
|
||||
max-width:1920px;max-height:1080px;margin:auto;
|
||||
}
|
||||
|
||||
/* background glows */
|
||||
.slide::before{
|
||||
content:"";position:absolute;width:760px;height:760px;border-radius:50%;
|
||||
background:radial-gradient(circle,rgba(59,130,246,.16),transparent 62%);
|
||||
top:-300px;right:-200px;pointer-events:none;
|
||||
}
|
||||
.slide::after{
|
||||
content:"";position:absolute;width:620px;height:620px;border-radius:50%;
|
||||
background:radial-gradient(circle,rgba(16,185,129,.08),transparent 65%);
|
||||
bottom:-280px;left:-160px;pointer-events:none;
|
||||
}
|
||||
|
||||
/* ===== TYPO ===== */
|
||||
.kicker{
|
||||
font-family:var(--mono);font-size:14px;letter-spacing:.28em;text-transform:uppercase;
|
||||
color:var(--accent2);margin-bottom:18px;display:flex;align-items:center;gap:12px;
|
||||
}
|
||||
.kicker::before{content:"";width:30px;height:2px;background:var(--accent);display:inline-block}
|
||||
h1{font-size:64px;font-weight:900;line-height:1.04;letter-spacing:-.02em}
|
||||
h2{font-size:44px;font-weight:800;line-height:1.08;letter-spacing:-.015em;margin-bottom:10px}
|
||||
.sub{color:var(--muted);font-size:20px;font-weight:400;line-height:1.5;max-width:980px}
|
||||
.slide-body{flex:1;min-height:0;margin-top:34px;display:flex;flex-direction:column}
|
||||
|
||||
/* ===== TITLE SLIDE ===== */
|
||||
.title-slide{justify-content:center}
|
||||
.title-slide h1{font-size:84px;max-width:1300px}
|
||||
.title-slide .lede{font-size:30px;color:var(--accent2);font-weight:600;margin-top:26px}
|
||||
.brandtag{
|
||||
display:inline-flex;align-items:center;gap:14px;
|
||||
font-family:var(--mono);font-size:16px;color:var(--muted);
|
||||
border:1px solid var(--border);border-radius:100px;padding:12px 22px;margin-bottom:40px;
|
||||
background:rgba(26,26,46,.6);width:max-content;
|
||||
}
|
||||
.dot{width:10px;height:10px;border-radius:50%;background:var(--success);box-shadow:0 0 14px var(--success)}
|
||||
.title-meta{
|
||||
margin-top:64px;display:flex;gap:64px;flex-wrap:wrap;
|
||||
}
|
||||
.title-meta div span{display:block;font-family:var(--mono);font-size:13px;color:var(--muted);letter-spacing:.1em;text-transform:uppercase;margin-bottom:6px}
|
||||
.title-meta div strong{font-size:24px;font-weight:700}
|
||||
|
||||
/* ===== CARDS / GRID ===== */
|
||||
.grid{display:grid;gap:22px}
|
||||
.g2{grid-template-columns:repeat(2,1fr)}
|
||||
.g3{grid-template-columns:repeat(3,1fr)}
|
||||
.g4{grid-template-columns:repeat(4,1fr)}
|
||||
.card{
|
||||
background:linear-gradient(160deg,var(--card),rgba(26,26,46,.55));
|
||||
border:1px solid var(--border);border-radius:18px;padding:28px 26px;
|
||||
position:relative;overflow:hidden;
|
||||
}
|
||||
.card .ic{
|
||||
width:46px;height:46px;border-radius:12px;display:flex;align-items:center;justify-content:center;
|
||||
font-size:22px;margin-bottom:16px;background:rgba(59,130,246,.14);border:1px solid rgba(59,130,246,.3);
|
||||
}
|
||||
.card h3{font-size:21px;font-weight:700;margin-bottom:8px}
|
||||
.card p{color:var(--muted);font-size:16px;line-height:1.55}
|
||||
.card.accent{border-color:rgba(59,130,246,.45)}
|
||||
.card.success .ic{background:rgba(16,185,129,.14);border-color:rgba(16,185,129,.35)}
|
||||
.card.warn .ic{background:rgba(245,158,11,.14);border-color:rgba(245,158,11,.35)}
|
||||
.tag{font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--accent2);font-weight:600}
|
||||
.pill{display:inline-block;font-family:var(--mono);font-size:13px;padding:4px 12px;border-radius:100px;border:1px solid var(--border);color:var(--muted)}
|
||||
.pill.rec{color:var(--success);border-color:rgba(16,185,129,.4);background:rgba(16,185,129,.08)}
|
||||
|
||||
/* ===== CODE / DIAGRAM BLOCK ===== */
|
||||
.codeblock{
|
||||
font-family:var(--mono);background:#0A0A12;border:1px solid var(--border);
|
||||
border-radius:16px;padding:30px 34px;font-size:18px;line-height:1.85;color:#CBD5E1;
|
||||
box-shadow:inset 0 0 0 1px rgba(59,130,246,.05);position:relative;
|
||||
}
|
||||
.codeblock .winbar{position:absolute;top:14px;left:18px;display:flex;gap:7px}
|
||||
.codeblock .winbar i{width:11px;height:11px;border-radius:50%;display:inline-block}
|
||||
.codeblock pre{margin-top:14px;white-space:pre;overflow:auto}
|
||||
.c-key{color:var(--accent2)} .c-ok{color:var(--success)} .c-warn{color:var(--warn)}
|
||||
.c-mut{color:var(--muted)} .c-acc{color:var(--accent);font-weight:700}
|
||||
|
||||
/* ===== ARCH MAP ===== */
|
||||
.archmap{display:flex;flex-direction:column;align-items:center;gap:0;font-family:var(--mono)}
|
||||
.node{
|
||||
border:1px solid var(--border);border-radius:14px;padding:16px 28px;background:var(--card);
|
||||
text-align:center;min-width:300px;
|
||||
}
|
||||
.node strong{display:block;font-size:20px;font-family:'Inter';font-weight:700}
|
||||
.node span{font-size:13px;color:var(--muted)}
|
||||
.node.master{border-color:var(--accent);box-shadow:0 0 32px rgba(59,130,246,.25);background:rgba(59,130,246,.08)}
|
||||
.node.git{border-color:rgba(96,165,250,.4)}
|
||||
.node.vps{border-color:rgba(16,185,129,.4);background:rgba(16,185,129,.05)}
|
||||
.arrow{color:var(--accent2);font-size:22px;padding:8px 0;font-family:var(--mono)}
|
||||
.arrow small{display:block;font-size:11px;color:var(--muted);letter-spacing:.1em}
|
||||
.tenants{display:flex;gap:18px;margin-top:6px}
|
||||
.tenant{
|
||||
border:1px dashed rgba(16,185,129,.4);border-radius:12px;padding:14px 20px;
|
||||
font-family:var(--mono);font-size:14px;text-align:center;background:rgba(16,185,129,.04);
|
||||
}
|
||||
.tenant b{color:var(--success)} .tenant span{display:block;color:var(--muted);font-size:12px;margin-top:4px}
|
||||
|
||||
/* ===== TABLE ===== */
|
||||
table{width:100%;border-collapse:collapse;font-size:18px}
|
||||
th,td{text-align:left;padding:18px 22px;border-bottom:1px solid var(--border)}
|
||||
th{font-family:var(--mono);font-size:13px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);font-weight:600}
|
||||
td{color:#E2E8F0}
|
||||
tr:last-child td{border-bottom:none}
|
||||
td .mono{font-family:var(--mono)}
|
||||
.num{font-family:var(--mono);font-weight:700;color:var(--accent2);text-align:right}
|
||||
.num.ok{color:var(--success)}
|
||||
tbody tr:hover{background:rgba(59,130,246,.05)}
|
||||
|
||||
/* ===== CHECKLIST ===== */
|
||||
.checklist{display:grid;grid-template-columns:1fr 1fr;gap:16px 38px;align-self:stretch}
|
||||
.check{
|
||||
display:flex;align-items:center;gap:18px;cursor:pointer;user-select:none;
|
||||
background:var(--card);border:1px solid var(--border);border-radius:14px;padding:20px 24px;
|
||||
transition:.25s;
|
||||
}
|
||||
.check:hover{border-color:rgba(59,130,246,.4);transform:translateX(3px)}
|
||||
.box{
|
||||
width:30px;height:30px;border-radius:8px;border:2px solid var(--muted);flex:0 0 auto;
|
||||
display:flex;align-items:center;justify-content:center;transition:.2s;font-weight:900;color:transparent;
|
||||
}
|
||||
.check.done .box{background:var(--success);border-color:var(--success);color:#06281c}
|
||||
.check.done .box::after{content:"✓"}
|
||||
.check .label{font-size:19px;font-weight:500;transition:.2s}
|
||||
.check.done .label{color:var(--muted);text-decoration:line-through}
|
||||
.check .mono{font-family:var(--mono);color:var(--accent2);font-size:16px}
|
||||
|
||||
/* ===== TIMELINE / ROADMAP ===== */
|
||||
.timeline{display:flex;align-items:stretch;gap:0;margin-top:30px}
|
||||
.phase{flex:1;position:relative;padding:0 14px}
|
||||
.phase .ring{
|
||||
width:26px;height:26px;border-radius:50%;border:3px solid var(--accent);
|
||||
background:var(--bg);margin:0 auto 22px;position:relative;z-index:2;
|
||||
}
|
||||
.phase.now .ring{background:var(--accent);box-shadow:0 0 22px var(--accent)}
|
||||
.phase::before{
|
||||
content:"";position:absolute;top:11px;left:-50%;width:100%;height:3px;
|
||||
background:var(--border);z-index:1;
|
||||
}
|
||||
.phase:first-child::before{display:none}
|
||||
.phase.now::before,.phase.now ~ .phase::before{background:var(--border)}
|
||||
.phase-card{
|
||||
background:var(--card);border:1px solid var(--border);border-radius:16px;padding:24px 22px;text-align:center;height:100%;
|
||||
}
|
||||
.phase.now .phase-card{border-color:var(--accent);background:rgba(59,130,246,.08)}
|
||||
.phase .ph-tag{font-family:var(--mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--accent2);margin-bottom:8px}
|
||||
.phase h3{font-size:22px;font-weight:700;margin-bottom:10px}
|
||||
.phase p{font-size:15px;color:var(--muted);line-height:1.5}
|
||||
|
||||
/* ===== ER DIAGRAM ===== */
|
||||
.er .codeblock{font-size:16px;line-height:1.75}
|
||||
|
||||
/* generic list */
|
||||
.blist{list-style:none;display:flex;flex-direction:column;gap:14px}
|
||||
.blist li{display:flex;gap:14px;font-size:19px;line-height:1.5;align-items:flex-start}
|
||||
.blist li::before{content:"▸";color:var(--accent);font-weight:900;margin-top:2px}
|
||||
.blist li b{color:var(--text)}
|
||||
.blist li span{color:var(--muted)}
|
||||
|
||||
/* steps for lifecycle */
|
||||
.steps{display:flex;flex-direction:column;gap:14px}
|
||||
.step{display:flex;gap:22px;align-items:center;background:var(--card);border:1px solid var(--border);border-radius:14px;padding:20px 26px}
|
||||
.step .no{font-family:var(--mono);font-size:26px;font-weight:700;color:var(--accent);width:46px;flex:0 0 auto}
|
||||
.step .txt{flex:1}
|
||||
.step .txt b{font-size:20px;font-weight:700;display:block}
|
||||
.step .txt span{color:var(--muted);font-size:16px}
|
||||
.step .time{font-family:var(--mono);font-size:15px;color:var(--success);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);border-radius:100px;padding:7px 16px;white-space:nowrap}
|
||||
|
||||
/* P&L econ */
|
||||
.pl{display:grid;grid-template-columns:1fr 1fr;gap:26px}
|
||||
.pl .block{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:30px}
|
||||
.pl .block.rev{border-color:rgba(16,185,129,.35)}
|
||||
.pl .block.cost{border-color:rgba(245,158,11,.35)}
|
||||
.pl .row{display:flex;justify-content:space-between;padding:12px 0;border-bottom:1px dashed var(--border);font-size:18px}
|
||||
.pl .row:last-child{border:none}
|
||||
.pl .row b{font-family:var(--mono)}
|
||||
.be{margin-top:24px;display:flex;gap:24px}
|
||||
.be .stat{flex:1;background:linear-gradient(160deg,rgba(59,130,246,.12),rgba(26,26,46,.6));border:1px solid rgba(59,130,246,.35);border-radius:16px;padding:24px 28px}
|
||||
.be .stat span{font-family:var(--mono);font-size:13px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
|
||||
.be .stat strong{display:block;font-size:38px;font-weight:800;color:var(--accent2);margin-top:6px}
|
||||
|
||||
/* ===== NAV ===== */
|
||||
.nav{position:fixed;bottom:26px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:18px;z-index:50}
|
||||
.nav button{
|
||||
width:46px;height:46px;border-radius:12px;border:1px solid var(--border);background:rgba(26,26,46,.85);
|
||||
color:var(--text);font-size:20px;cursor:pointer;transition:.2s;backdrop-filter:blur(8px);
|
||||
}
|
||||
.nav button:hover{border-color:var(--accent);color:var(--accent2)}
|
||||
.nav .counter{font-family:var(--mono);font-size:15px;color:var(--muted);min-width:64px;text-align:center}
|
||||
.nav .counter b{color:var(--text)}
|
||||
.progress{position:fixed;top:0;left:0;height:3px;background:linear-gradient(90deg,var(--accent),var(--success));z-index:60;transition:width .5s ease;box-shadow:0 0 10px var(--accent)}
|
||||
.brandmark{position:fixed;bottom:30px;left:40px;font-family:var(--mono);font-size:13px;color:var(--muted);z-index:50;letter-spacing:.1em}
|
||||
.pagenum{position:fixed;bottom:30px;right:40px;font-family:var(--mono);font-size:13px;color:var(--muted);z-index:50}
|
||||
|
||||
.twocol{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center;height:100%}
|
||||
.footnote{margin-top:auto;padding-top:22px;color:var(--muted);font-size:15px;font-family:var(--mono)}
|
||||
|
||||
@media(max-width:1100px){
|
||||
.slide{padding:40px 44px 90px}
|
||||
h1{font-size:48px}.title-slide h1{font-size:56px}h2{font-size:34px}
|
||||
.g4{grid-template-columns:repeat(2,1fr)}.g3{grid-template-columns:repeat(2,1fr)}
|
||||
.checklist{grid-template-columns:1fr}.timeline{flex-direction:column;gap:14px}
|
||||
.phase::before{display:none}.twocol{grid-template-columns:1fr;gap:24px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="progress" id="progress"></div>
|
||||
<div class="deck" id="deck">
|
||||
|
||||
<!-- 1 TITLE -->
|
||||
<section class="slide title-slide active">
|
||||
<div class="stage" style="display:flex;flex-direction:column;justify-content:center">
|
||||
<div class="brandtag"><span class="dot"></span> @wasrusgen1 CRM · PLATFORM ARCHITECTURE</div>
|
||||
<h1>Архитектура платформы<br><span style="color:var(--accent2)">@wasrusgen1 CRM</span></h1>
|
||||
<p class="lede">Один продукт — много клиентов. Полный контроль.</p>
|
||||
<div class="title-meta">
|
||||
<div><span>Документ для</span><strong>Собственника системы</strong></div>
|
||||
<div><span>Тема</span><strong>Ввод проекта в работу</strong></div>
|
||||
<div><span>Дата</span><strong>2026</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 2 SYSTEM MAP -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 02 · Big picture</div>
|
||||
<h2>Карта системы</h2>
|
||||
<p class="sub">Единый код в GitHub разворачивается на VPS. Каждый клиент — отдельный поддомен и отдельная база.</p>
|
||||
<div class="slide-body" style="justify-content:center">
|
||||
<div class="archmap">
|
||||
<div class="node master"><strong>Руслан · Мастер-панель</strong><span>управление всеми проектами</span></div>
|
||||
<div class="arrow">↓</div>
|
||||
<div class="node git"><strong>GitHub</strong><span>единый исходный код</span></div>
|
||||
<div class="arrow">↓ <small>авто-деплой CI/CD</small></div>
|
||||
<div class="node vps"><strong>VPS сервер · Nginx + Docker</strong><span>роутинг по поддоменам</span></div>
|
||||
<div class="arrow">↓</div>
|
||||
<div class="tenants">
|
||||
<div class="tenant"><b>salon1.crm.ru</b><span>→ PostgreSQL DB_1</span></div>
|
||||
<div class="tenant"><b>salon2.crm.ru</b><span>→ PostgreSQL DB_2</span></div>
|
||||
<div class="tenant"><b>salonN.crm.ru</b><span>→ PostgreSQL DB_N</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 3 THREE MODELS -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 03 · Стратегия</div>
|
||||
<h2>Три модели управления</h2>
|
||||
<p class="sub">Как обслуживать множество клиентов на одной кодовой базе.</p>
|
||||
<div class="slide-body">
|
||||
<div class="grid g3" style="flex:1;align-content:center">
|
||||
<div class="card">
|
||||
<div class="ic">A</div>
|
||||
<h3>Мультитенант</h3>
|
||||
<p>Один деплой, одна БД, разделение по <span class="mono" style="color:var(--accent2)">tenant_id</span>. Дёшево в масштабе, но слабая изоляция и риск общего сбоя.</p>
|
||||
<div style="margin-top:14px"><span class="pill">сложно стартовать</span></div>
|
||||
</div>
|
||||
<div class="card success">
|
||||
<div class="ic">B</div>
|
||||
<h3>Мультидеплой</h3>
|
||||
<p>Отдельный контейнер + отдельная БД на клиента. Полная изоляция, простой старт, легко считать расходы по клиенту.</p>
|
||||
<div style="margin-top:14px"><span class="pill rec">★ старт здесь</span></div>
|
||||
</div>
|
||||
<div class="card accent">
|
||||
<div class="ic">C</div>
|
||||
<h3>Гибрид</h3>
|
||||
<p>Общий код и инфра-слой, изолированные БД, общий мониторинг и единое обновление. Баланс цены и изоляции на росте.</p>
|
||||
<div style="margin-top:14px"><span class="pill rec">★ цель</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footnote">Рекомендация: начать с модели <b style="color:var(--success)">B (мультидеплой)</b> → перейти на <b style="color:var(--accent2)">C (гибрид)</b> при 5+ клиентах.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 4 LIFECYCLE -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 04 · Процесс</div>
|
||||
<h2>Жизненный цикл нового проекта</h2>
|
||||
<p class="sub">От подписи договора до запуска — около 3 рабочих дней.</p>
|
||||
<div class="slide-body">
|
||||
<div class="steps" style="margin:auto 0">
|
||||
<div class="step"><div class="no">01</div><div class="txt"><b>Договор с клиентом</b><span>согласование условий, тариф, доступы</span></div><div class="time">1 день</div></div>
|
||||
<div class="step"><div class="no">02</div><div class="txt"><b>Настройка инстанса</b><span>создание БД, конфиг, поддомен, деплой</span></div><div class="time">2–4 ч</div></div>
|
||||
<div class="step"><div class="no">03</div><div class="txt"><b>Загрузка данных</b><span>салоны, пользователи, справочники</span></div><div class="time">1–2 ч</div></div>
|
||||
<div class="step"><div class="no">04</div><div class="txt"><b>Обучение команды</b><span>КД, администраторы, мастера</span></div><div class="time">1–2 дня</div></div>
|
||||
<div class="step"><div class="no">05</div><div class="txt"><b>Старт → техподдержка</b><span>пилот, мониторинг, SLA</span></div><div class="time">запуск</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 5 STACK -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 05 · Технологии</div>
|
||||
<h2>Стек технологий</h2>
|
||||
<p class="sub">Проверенные инструменты, без экзотики — быстрый найм и поддержка.</p>
|
||||
<div class="slide-body">
|
||||
<div class="grid g4" style="flex:1;align-content:center">
|
||||
<div class="card"><div class="ic">🖥️</div><h3>Frontend</h3><p>HTML/JS (текущий прототип) → <b style="color:var(--accent2)">React</b> в v2</p></div>
|
||||
<div class="card"><div class="ic">⚙️</div><h3>Backend</h3><p>Node.js + Express — REST API, авторизация</p></div>
|
||||
<div class="card success"><div class="ic">🗄️</div><h3>База данных</h3><p>PostgreSQL — <b style="color:var(--success)">одна БД на клиента</b></p></div>
|
||||
<div class="card"><div class="ic">⚡</div><h3>Кеш / сессии</h3><p>Redis — сессии, очереди, кеш</p></div>
|
||||
<div class="card"><div class="ic">📦</div><h3>Файлы</h3><p>S3 / MinIO — фото, документы, экспорты</p></div>
|
||||
<div class="card accent"><div class="ic">🚀</div><h3>Деплой</h3><p>Docker Compose + GitHub Actions CI/CD</p></div>
|
||||
<div class="card"><div class="ic">☁️</div><h3>Хостинг</h3><p>VPS: Hetzner / Timeweb / Selectel</p></div>
|
||||
<div class="card"><div class="ic">🔒</div><h3>Сеть</h3><p>Nginx reverse-proxy + Let's Encrypt HTTPS</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 6 ER DIAGRAM -->
|
||||
<section class="slide er">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 06 · Данные</div>
|
||||
<h2>Схема базы данных</h2>
|
||||
<p class="sub">8 ключевых сущностей. Каждый клиент получает изолированную копию этой схемы.</p>
|
||||
<div class="slide-body" style="justify-content:center">
|
||||
<div class="codeblock">
|
||||
<div class="winbar"><i style="background:#ff5f57"></i><i style="background:#febc2e"></i><i style="background:#28c840"></i></div>
|
||||
<pre><span class="c-mut"># SCHEMA.md — ER-диаграмма (одна БД на клиента)</span>
|
||||
|
||||
<span class="c-acc">┌────────────┐</span <span class="c-acc">┌────────────┐</span <span class="c-acc">┌────────────┐</span
|
||||
<span class="c-acc">│ salons │</span<span class="c-key">──1:N──</span<span class="c-acc">│ users │</span<span class="c-key">──1:N──</span<span class="c-acc">│shiftRequest│</span
|
||||
<span class="c-acc">└─────┬──────┘</span <span class="c-acc">└─────┬──────┘</span <span class="c-acc">└────────────┘</span
|
||||
<span class="c-key">│ 1:N</span <span class="c-key">│ 1:N</span
|
||||
<span class="c-acc">┌─────┴──────┐</span <span class="c-acc">┌─────┴──────┐</span <span class="c-acc">┌────────────┐</span
|
||||
<span class="c-acc">│ clients │</span<span class="c-key">──1:N──</span<span class="c-acc">│ orders │</span<span class="c-key">──1:N──</span<span class="c-acc">│ ratings │</span
|
||||
<span class="c-acc">└─────┬──────┘</span <span class="c-acc">└─────┬──────┘</span <span class="c-acc">└────────────┘</span
|
||||
<span class="c-key">│ 1:N</span <span class="c-key">│ 1:N</span
|
||||
<span class="c-acc">┌─────┴──────┐</span <span class="c-acc">┌─────┴──────┐</span
|
||||
<span class="c-acc">│appointments│</span<span class="c-key">───────│</span <span class="c-acc">│ requests │</span
|
||||
<span class="c-acc">└────────────┘</span <span class="c-acc">└────────────┘</span
|
||||
|
||||
<span class="c-ok">salons</span> → сеть салонов клиента
|
||||
<span class="c-ok">users</span> → сотрудники (КД / админ / мастер)
|
||||
<span class="c-ok">clients</span> → клиентская база салона
|
||||
<span class="c-ok">orders</span> → сделки / заказы
|
||||
<span class="c-ok">appointments</span>→ записи на услуги
|
||||
<span class="c-ok">requests</span> → заявки / лиды
|
||||
<span class="c-ok">shiftRequests</span>→ заявки на смены сотрудников
|
||||
<span class="c-ok">ratings</span> → оценки и обратная связь</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 7 MASTER PANEL -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 07 · Инструмент владельца</div>
|
||||
<h2>Мастер-панель Руслана</h2>
|
||||
<p class="sub">Что нужно разработать для управления всем парком проектов из одного места.</p>
|
||||
<div class="slide-body">
|
||||
<div class="grid g2" style="flex:1;align-content:center">
|
||||
<div class="card accent"><div class="ic">📋</div><h3>Список проектов</h3><p>Все клиентские инстансы: статус, last activity, число пользователей, тариф.</p></div>
|
||||
<div class="card success"><div class="ic">➕</div><h3>Создать новый проект</h3><p>Кнопка «Новый проект» → деплой инстанса в 1 клик (или скриптом).</p></div>
|
||||
<div class="card"><div class="ic">📡</div><h3>Мониторинг</h3><p>Uptime, ошибки, нагрузка CPU/RAM, статус БД по каждому клиенту.</p></div>
|
||||
<div class="card"><div class="ic">🔄</div><h3>Массовое обновление</h3><p>Выкатить новую версию на все проекты одновременно одной командой.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 8 CHECKLIST -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 08 · Onboarding</div>
|
||||
<h2>Как подключить нового клиента</h2>
|
||||
<p class="sub">Интерактивный чеклист — кликните по пунктам. <span style="color:var(--accent2)">Прогресс сохраняется в сессии.</span></p>
|
||||
<div class="slide-body" style="justify-content:center">
|
||||
<div class="checklist" id="checklist">
|
||||
<div class="check"><div class="box"></div><div class="label">Подписан договор</div></div>
|
||||
<div class="check"><div class="box"></div><div class="label">Получены данные: сеть, салоны, сотрудники</div></div>
|
||||
<div class="check"><div class="box"></div><div class="label">Создан поддомен <span class="mono">client-name.wasrusgen1.ru</span></div></div>
|
||||
<div class="check"><div class="box"></div><div class="label">Развёрнута БД и заполнены справочники</div></div>
|
||||
<div class="check"><div class="box"></div><div class="label">Созданы учётные записи (КД, Администраторы)</div></div>
|
||||
<div class="check"><div class="box"></div><div class="label">Проведено обучение команды</div></div>
|
||||
<div class="check"><div class="box"></div><div class="label">Запущен пилот</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 9 INFRA COST -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 09 · Инфраструктура</div>
|
||||
<h2>Инфраструктура и стоимость</h2>
|
||||
<p class="sub">Расходы растут вместе с числом клиентов — на старте почти нулевые.</p>
|
||||
<div class="slide-body" style="justify-content:center">
|
||||
<div class="card" style="padding:8px 16px">
|
||||
<table>
|
||||
<thead><tr><th>Этап</th><th>Конфигурация</th><th style="text-align:right">$/мес</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><b>Прототип</b> <span style="color:var(--muted)">(сейчас)</span></td><td class="mono">GitHub Pages</td><td class="num ok">$0</td></tr>
|
||||
<tr><td><b>Пилот</b> <span style="color:var(--muted)">(1–3 клиента)</span></td><td class="mono">VPS 2CPU / 4GB + Postgres</td><td class="num">~$25–35</td></tr>
|
||||
<tr><td><b>Рост</b> <span style="color:var(--muted)">(4–10 клиентов)</span></td><td class="mono">VPS 4CPU / 8GB + backup</td><td class="num">~$50–80</td></tr>
|
||||
<tr><td><b>Масштаб</b> <span style="color:var(--muted)">(10+ клиентов)</span></td><td class="mono">2 VPS + балансировщик</td><td class="num">~$120–200</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footnote">При 8 000 ₽/мес с клиента стоимость инфраструктуры — менее <b style="color:var(--success)">3 %</b> от выручки даже на масштабе.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 10 ECONOMICS -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 10 · Экономика</div>
|
||||
<h2>Экономика продукта · P&L</h2>
|
||||
<p class="sub">Точка безубыточности — первый же клиент. Маржа растёт с каждым следующим.</p>
|
||||
<div class="slide-body" style="justify-content:center">
|
||||
<div class="pl">
|
||||
<div class="block cost">
|
||||
<div class="tag" style="color:var(--warn)">— РАСХОДЫ / мес</div>
|
||||
<div class="row"><span>Сервер (VPS + Postgres)</span><b>~$35</b></div>
|
||||
<div class="row"><span>Поддержка (N часов)</span><b>переменные</b></div>
|
||||
<div class="row"><span>Прочее (домены, бэкап)</span><b>~$5</b></div>
|
||||
<div class="row" style="color:var(--warn)"><span><b>Итого база</b></span><b>~$40</b></div>
|
||||
</div>
|
||||
<div class="block rev">
|
||||
<div class="tag" style="color:var(--success)">+ ДОХОДЫ / мес</div>
|
||||
<div class="row"><span>Тариф на клиента</span><b>8 000 ₽</b></div>
|
||||
<div class="row"><span>5 клиентов</span><b>40 000 ₽</b></div>
|
||||
<div class="row"><span>10 клиентов</span><b>80 000 ₽</b></div>
|
||||
<div class="row" style="color:var(--success)"><span><b>Модель X × 8 000 ₽</b></span><b>растёт линейно</b></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="be">
|
||||
<div class="stat"><span>Break-even</span><strong>1 клиент</strong></div>
|
||||
<div class="stat"><span>Целевая маржа</span><strong>85 %+</strong></div>
|
||||
<div class="stat"><span>При</span><strong>5+ клиентах</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 11 ROADMAP -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 11 · Roadmap</div>
|
||||
<h2>Roadmap продукта</h2>
|
||||
<p class="sub">Четыре фазы — от прототипа до AI-функций.</p>
|
||||
<div class="slide-body" style="justify-content:center">
|
||||
<div class="timeline">
|
||||
<div class="phase now">
|
||||
<div class="ring"></div>
|
||||
<div class="phase-card"><div class="ph-tag">Фаза 1 · сейчас</div><h3>Прототип</h3><p>Готовый интерфейс → первый пилотный клиент.</p></div>
|
||||
</div>
|
||||
<div class="phase">
|
||||
<div class="ring"></div>
|
||||
<div class="phase-card"><div class="ph-tag">Фаза 2</div><h3>Бэкенд</h3><p>Node.js + PostgreSQL + авторизация (JWT).</p></div>
|
||||
</div>
|
||||
<div class="phase">
|
||||
<div class="ring"></div>
|
||||
<div class="phase-card"><div class="ph-tag">Фаза 3</div><h3>Мобильное (PWA)</h3><p>Установка на телефон, офлайн, push-уведомления.</p></div>
|
||||
</div>
|
||||
<div class="phase">
|
||||
<div class="ring"></div>
|
||||
<div class="phase-card"><div class="ph-tag">Фаза 4</div><h3>AI-функции</h3><p>Прогноз сделок, автоматизация рутины, аналитика.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 12 SECURITY -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 12 · Безопасность</div>
|
||||
<h2>Безопасность и изоляция данных</h2>
|
||||
<p class="sub">Данные каждого клиента физически отделены — утечка одного не затрагивает других.</p>
|
||||
<div class="slide-body">
|
||||
<div class="grid g3" style="flex:1;align-content:center">
|
||||
<div class="card success"><div class="ic">🧱</div><h3>Физическая изоляция</h3><p>Каждый клиент — отдельная БД. Данные не пересекаются на уровне хранилища.</p></div>
|
||||
<div class="card"><div class="ic">🔑</div><h3>JWT авторизация</h3><p>Токены доступа, роли и права на уровне API.</p></div>
|
||||
<div class="card"><div class="ic">🔐</div><h3>HTTPS везде</h3><p>Let's Encrypt, авто-обновление сертификатов на всех поддоменах.</p></div>
|
||||
<div class="card"><div class="ic">💾</div><h3>Бэкапы ежедневно</h3><p>Автоматический бэкап каждой БД, хранение и восстановление.</p></div>
|
||||
<div class="card accent"><div class="ic">📤</div><h3>Экспорт данных</h3><p>Клиент может запросить выгрузку своих данных в любой момент.</p></div>
|
||||
<div class="card"><div class="ic">📜</div><h3>Соответствие ПДн</h3><p>Хранение в РФ, разграничение доступа, журналирование.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 13 SLA -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 13 · Обязательства</div>
|
||||
<h2>SLA и поддержка</h2>
|
||||
<p class="sub">Понятные обязательства перед клиентом — основа доверия и удержания.</p>
|
||||
<div class="slide-body">
|
||||
<div class="grid g4" style="flex:1;align-content:center">
|
||||
<div class="card success"><div class="ic">⏱️</div><h3>Uptime</h3><p style="font-size:34px;font-weight:800;color:var(--success);font-family:var(--mono)">99.5 %</p></div>
|
||||
<div class="card"><div class="ic">📞</div><h3>Реакция</h3><p style="font-size:34px;font-weight:800;color:var(--accent2);font-family:var(--mono)">4 ч</p><p>в рабочее время</p></div>
|
||||
<div class="card"><div class="ic">🔁</div><h3>Обновления</h3><p style="font-size:34px;font-weight:800;color:var(--accent2);font-family:var(--mono)">2 нед</p><p>регулярный цикл</p></div>
|
||||
<div class="card accent"><div class="ic">🚀</div><h3>Onboarding</h3><p style="font-size:34px;font-weight:800;color:var(--accent);font-family:var(--mono)">3 дня</p><p>до запуска</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 14 NEXT STEPS -->
|
||||
<section class="slide">
|
||||
<div class="stage">
|
||||
<div class="kicker">Слайд 14 · План действий</div>
|
||||
<h2>Следующие шаги</h2>
|
||||
<p class="sub">Конкретный план на 4 недели — от выбора клиента до запуска пилота.</p>
|
||||
<div class="slide-body">
|
||||
<div class="steps" style="margin:auto 0">
|
||||
<div class="step"><div class="no">W1</div><div class="txt"><b>Выбрать первого пилотного клиента</b><span>сеть салонов, готовая дать данные и команду</span></div><div class="time">Неделя 1</div></div>
|
||||
<div class="step"><div class="no">W2</div><div class="txt"><b>Развернуть бэкенд на VPS</b><span>Node.js + PostgreSQL + Docker + CI/CD</span></div><div class="time">Неделя 2</div></div>
|
||||
<div class="step"><div class="no">W3</div><div class="txt"><b>Перевести прототип на реальные данные</b><span>подключить API, загрузить салоны и пользователей</span></div><div class="time">Неделя 3</div></div>
|
||||
<div class="step"><div class="no">W4</div><div class="txt"><b>Запуск пилота с командой клиента</b><span>обучение, мониторинг, сбор обратной связи</span></div><div class="time">Неделя 4</div></div>
|
||||
</div>
|
||||
<div class="footnote">Готов начать с недели 1 — нужен выбор пилотного клиента.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="brandmark">@wasrusgen1 · CRM PLATFORM</div>
|
||||
<div class="pagenum" id="pagenum">01 / 14</div>
|
||||
|
||||
<nav class="nav">
|
||||
<button id="prev" aria-label="Назад">‹</button>
|
||||
<div class="counter"><b id="cur">1</b> / 14</div>
|
||||
<button id="next" aria-label="Вперёд">›</button>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const slides=[...document.querySelectorAll('.slide')];
|
||||
let i=0;const total=slides.length;
|
||||
const progress=document.getElementById('progress');
|
||||
const cur=document.getElementById('cur');
|
||||
const pagenum=document.getElementById('pagenum');
|
||||
function pad(n){return String(n).padStart(2,'0');}
|
||||
function show(n){
|
||||
i=Math.max(0,Math.min(total-1,n));
|
||||
slides.forEach((s,k)=>s.classList.toggle('active',k===i));
|
||||
progress.style.width=((i+1)/total*100)+'%';
|
||||
cur.textContent=i+1;
|
||||
pagenum.textContent=pad(i+1)+' / '+pad(total);
|
||||
}
|
||||
document.getElementById('next').onclick=()=>show(i+1);
|
||||
document.getElementById('prev').onclick=()=>show(i-1);
|
||||
document.addEventListener('keydown',e=>{
|
||||
if(['ArrowRight','PageDown',' '].includes(e.key)){e.preventDefault();show(i+1);}
|
||||
else if(['ArrowLeft','PageUp'].includes(e.key)){e.preventDefault();show(i-1);}
|
||||
else if(e.key==='Home')show(0);
|
||||
else if(e.key==='End')show(total-1);
|
||||
});
|
||||
// interactive checklist
|
||||
document.querySelectorAll('#checklist .check').forEach(c=>{
|
||||
c.addEventListener('click',()=>c.classList.toggle('done'));
|
||||
});
|
||||
show(0);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
634
Mokap/presentation_product.html
Normal file
634
Mokap/presentation_product.html
Normal file
@ -0,0 +1,634 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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;900&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--bg:#0F0F1A;
|
||||
--primary:#047857;
|
||||
--dark:#064E3B;
|
||||
--mid:#10B981;
|
||||
--text:#F8FAFC;
|
||||
--card:#1A1A2E;
|
||||
--card-2:#21213a;
|
||||
--muted:#94A3B8;
|
||||
--line:rgba(255,255,255,.08);
|
||||
--line-2:rgba(255,255,255,.14);
|
||||
}
|
||||
html,body{height:100%}
|
||||
body{
|
||||
font-family:'Inter',sans-serif;
|
||||
background:var(--bg);
|
||||
color:var(--text);
|
||||
overflow:hidden;
|
||||
-webkit-font-smoothing:antialiased;
|
||||
}
|
||||
|
||||
/* ── Deck shell ─────────────────────────────────── */
|
||||
.deck{position:fixed;inset:0;}
|
||||
.slide{
|
||||
position:absolute;inset:0;
|
||||
display:none;
|
||||
flex-direction:column;
|
||||
justify-content:center;
|
||||
padding:5vh 6.5vw 9vh;
|
||||
opacity:0;
|
||||
transition:opacity .45s ease;
|
||||
}
|
||||
.slide.active{display:flex;opacity:1;animation:fadeUp .5s ease both}
|
||||
@keyframes fadeUp{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}}
|
||||
|
||||
/* gradient blobs */
|
||||
.slide::before{content:'';position:absolute;top:-15vh;right:-10vw;width:46vw;height:46vw;
|
||||
background:radial-gradient(circle,rgba(16,185,129,.13)0%,transparent 70%);pointer-events:none;z-index:0}
|
||||
.slide::after{content:'';position:absolute;bottom:-12vh;left:-6vw;width:34vw;height:34vw;
|
||||
background:radial-gradient(circle,rgba(4,120,87,.18)0%,transparent 70%);pointer-events:none;z-index:0}
|
||||
.slide>*{position:relative;z-index:1}
|
||||
|
||||
/* ── Typography ─────────────────────────────────── */
|
||||
.kicker{display:inline-flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;
|
||||
text-transform:uppercase;letter-spacing:.14em;color:var(--mid);margin-bottom:1.4vh}
|
||||
.kicker::before{content:'';width:26px;height:2px;background:var(--mid);border-radius:2px}
|
||||
h1.title{font-family:'Montserrat',sans-serif;font-weight:800;font-size:clamp(2.2rem,4.6vw,4.4rem);
|
||||
line-height:1.04;letter-spacing:-.02em;margin-bottom:2vh}
|
||||
h2.title{font-family:'Montserrat',sans-serif;font-weight:800;font-size:clamp(1.8rem,3.4vw,3rem);
|
||||
line-height:1.08;letter-spacing:-.02em;margin-bottom:1vh}
|
||||
.lead{font-size:clamp(1rem,1.5vw,1.35rem);color:var(--muted);max-width:48ch;line-height:1.55}
|
||||
.grad{background:linear-gradient(100deg,var(--mid),var(--primary));-webkit-background-clip:text;background-clip:text;color:transparent}
|
||||
|
||||
/* ── Cover ──────────────────────────────────────── */
|
||||
#s1{background:linear-gradient(150deg,#022C22 0%,#064E3B 42%,#047857 120%);justify-content:center}
|
||||
#s1::before{top:-22vh;right:-16vw;width:60vw;height:60vw;background:radial-gradient(circle,rgba(16,185,129,.22)0%,transparent 68%)}
|
||||
.brand-row{display:flex;align-items:center;gap:14px;margin-bottom:3.4vh}
|
||||
.at-badge{width:54px;height:54px;border-radius:14px;background:rgba(255,255,255,.12);
|
||||
border:1.5px solid rgba(255,255,255,.22);display:flex;align-items:center;justify-content:center}
|
||||
.at-badge svg{width:32px;height:32px}
|
||||
.brand-stack{display:flex;flex-direction:column;line-height:1}
|
||||
.brand-handle{font-family:'Montserrat',sans-serif;font-size:.8rem;font-weight:500;
|
||||
letter-spacing:.05em;color:rgba(255,255,255,.55);margin-bottom:5px}
|
||||
.brand-name{font-family:'Montserrat',sans-serif;font-weight:900;font-size:1.7rem;letter-spacing:-1px;color:#fff}
|
||||
#s1 h1{font-family:'Montserrat',sans-serif;font-weight:800;font-size:clamp(2.6rem,5.4vw,5rem);
|
||||
line-height:1.05;letter-spacing:-.025em;color:#fff;max-width:18ch;margin-bottom:2.4vh}
|
||||
#s1 .cover-sub{font-size:clamp(1.05rem,1.7vw,1.5rem);color:rgba(255,255,255,.72);max-width:42ch;margin-bottom:5vh}
|
||||
.cover-meta{display:flex;gap:3vw;flex-wrap:wrap}
|
||||
.cover-meta b{display:block;font-size:1rem;color:#fff;font-weight:600;margin-bottom:3px}
|
||||
.cover-meta span{font-size:.85rem;color:rgba(255,255,255,.55)}
|
||||
|
||||
/* ── generic cards ──────────────────────────────── */
|
||||
.card{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:1.6vw 1.7vw}
|
||||
.card .ic{width:46px;height:46px;border-radius:12px;display:flex;align-items:center;justify-content:center;
|
||||
font-size:1.4rem;margin-bottom:14px}
|
||||
.ic.green{background:rgba(16,185,129,.14);}
|
||||
.ic.red{background:rgba(239,68,68,.14)}
|
||||
.card h3{font-size:1.12rem;font-weight:700;margin-bottom:7px;letter-spacing:-.01em}
|
||||
.card p{font-size:.92rem;color:var(--muted);line-height:1.5}
|
||||
|
||||
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:1.4vw}
|
||||
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:1.2vw}
|
||||
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:1.1vw}
|
||||
.grid-5{display:grid;grid-template-columns:repeat(5,1fr);gap:1vw}
|
||||
.mt{margin-top:3vh}
|
||||
|
||||
.head-block{margin-bottom:3.4vh;max-width:62ch}
|
||||
|
||||
/* problem vs solution flow */
|
||||
.flow{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
|
||||
.flow-node{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;
|
||||
font-size:.9rem;font-weight:600;flex:1;min-width:120px;text-align:center}
|
||||
.flow-node.bad{border-color:rgba(239,68,68,.4);color:#FCA5A5}
|
||||
.flow-node.good{border-color:rgba(16,185,129,.45);background:rgba(16,185,129,.08);color:#A7F3D0}
|
||||
.flow-arrow{color:var(--muted);font-size:1.3rem}
|
||||
.flow-hub{background:linear-gradient(135deg,var(--primary),var(--dark));border:none;
|
||||
border-radius:16px;padding:20px;text-align:center;flex:1.3;min-width:160px}
|
||||
.flow-hub .h-t{font-family:'Montserrat',sans-serif;font-weight:800;font-size:1.05rem;color:#fff}
|
||||
.flow-hub .h-s{font-size:.78rem;color:rgba(255,255,255,.7);margin-top:4px}
|
||||
|
||||
/* roles */
|
||||
.role-card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:1.5vh 1.2vw;
|
||||
display:flex;flex-direction:column;gap:8px;transition:.2s}
|
||||
.role-card:hover{border-color:var(--mid);transform:translateY(-3px)}
|
||||
.role-ic{width:44px;height:44px;border-radius:11px;background:rgba(16,185,129,.14);
|
||||
display:flex;align-items:center;justify-content:center;font-size:1.35rem}
|
||||
.role-card h3{font-size:1.02rem;font-weight:700}
|
||||
.role-card .zone{font-size:.82rem;color:var(--mid);font-weight:600}
|
||||
.role-card p{font-size:.82rem;color:var(--muted);line-height:1.45}
|
||||
|
||||
/* pipeline */
|
||||
.pipe{display:flex;align-items:stretch;gap:0;flex-wrap:nowrap;overflow:visible}
|
||||
.pstep{flex:1;background:var(--card);border:1px solid var(--line);position:relative;
|
||||
padding:2vh 0.6vw;text-align:center;display:flex;flex-direction:column;align-items:center;gap:8px}
|
||||
.pstep:first-child{border-radius:14px 0 0 14px}
|
||||
.pstep:last-child{border-radius:0 14px 14px 0}
|
||||
.pstep+.pstep{border-left:none}
|
||||
.pstep .pn{font-family:'Montserrat',sans-serif;font-weight:800;font-size:.8rem;color:var(--mid);opacity:.7}
|
||||
.pstep .pic{font-size:1.5rem}
|
||||
.pstep .pl{font-size:.84rem;font-weight:600;line-height:1.15}
|
||||
.pstep::after{content:'';position:absolute;right:-9px;top:50%;transform:translateY(-50%) rotate(45deg);
|
||||
width:16px;height:16px;background:var(--card);border-top:1px solid var(--line);border-right:1px solid var(--line);z-index:2}
|
||||
.pstep:last-child::after{display:none}
|
||||
.pstep.hl{background:linear-gradient(160deg,var(--primary),var(--dark));border-color:transparent}
|
||||
.pstep.hl .pn,.pstep.hl .pl{color:#fff}
|
||||
.pstep.hl::after{background:var(--primary);border-color:transparent}
|
||||
|
||||
/* dashboard */
|
||||
.dash{display:grid;grid-template-columns:1.3fr 1fr;gap:1.3vw}
|
||||
.panel{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:1.6vh 1.3vw}
|
||||
.panel-h{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.09em;color:var(--muted);margin-bottom:1.4vh}
|
||||
.kpi-row{display:grid;grid-template-columns:repeat(2,1fr);gap:.9vw;margin-bottom:1.4vh}
|
||||
.kpi{background:var(--card-2);border-radius:12px;padding:1.3vh 1vw}
|
||||
.kpi .v{font-family:'Montserrat',sans-serif;font-weight:800;font-size:1.5rem;letter-spacing:-.02em}
|
||||
.kpi .l{font-size:.74rem;color:var(--muted);margin-top:3px}
|
||||
.kpi .d{font-size:.74rem;font-weight:700;margin-top:5px}
|
||||
.up{color:var(--mid)}.down{color:#FCA5A5}
|
||||
.bar-row{display:flex;align-items:center;gap:10px;margin-bottom:11px}
|
||||
.bar-row .bl{font-size:.82rem;color:var(--muted);width:90px;flex-shrink:0}
|
||||
.bar-track{flex:1;height:9px;background:var(--card-2);border-radius:6px;overflow:hidden}
|
||||
.bar-fill{height:100%;background:linear-gradient(90deg,var(--primary),var(--mid));border-radius:6px}
|
||||
.bar-row .bv{font-size:.8rem;font-weight:700;width:46px;text-align:right}
|
||||
table{width:100%;border-collapse:collapse}
|
||||
th{font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);text-align:left;padding:6px 6px;font-weight:700}
|
||||
td{font-size:.84rem;padding:8px 6px;border-top:1px solid var(--line)}
|
||||
.pill{display:inline-block;font-size:.72rem;font-weight:700;padding:3px 9px;border-radius:20px}
|
||||
.pill.ok{background:rgba(16,185,129,.16);color:var(--mid)}
|
||||
.pill.warn{background:rgba(251,191,36,.16);color:#FCD34D}
|
||||
.pill.late{background:rgba(239,68,68,.16);color:#FCA5A5}
|
||||
|
||||
/* managers / schedule grid */
|
||||
.sched{width:100%;border-collapse:collapse;font-size:.8rem}
|
||||
.sched th,.sched td{border:1px solid var(--line);padding:7px 8px;text-align:center}
|
||||
.sched th{background:var(--card-2);color:var(--muted)}
|
||||
.cell-busy{background:rgba(4,120,87,.32);color:#fff;font-weight:600;border-radius:0}
|
||||
.cell-free{color:var(--muted);opacity:.5}
|
||||
.cell-gps{background:rgba(16,185,129,.18);color:var(--mid);font-weight:600}
|
||||
|
||||
/* rating */
|
||||
.rate-card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:1.7vh 1.3vw}
|
||||
.stars{color:var(--mid);font-size:1.15rem;letter-spacing:2px}
|
||||
.scorebig{font-family:'Montserrat',sans-serif;font-weight:800;font-size:2.4rem;letter-spacing:-.02em}
|
||||
.scorebig small{font-size:1rem;color:var(--muted);font-weight:600}
|
||||
.crit{display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-top:1px solid var(--line);font-size:.86rem}
|
||||
.crit .cn{color:var(--muted)}
|
||||
.crit .cs{font-weight:700}
|
||||
|
||||
/* KB articles */
|
||||
.kb-card{background:var(--card);border:1px solid var(--line);border-radius:16px;overflow:hidden;display:flex;flex-direction:column}
|
||||
.kb-cover{height:90px;background:linear-gradient(135deg,var(--primary),var(--dark));
|
||||
display:flex;align-items:center;justify-content:center;font-size:2rem}
|
||||
.kb-body{padding:1.4vh 1vw;display:flex;flex-direction:column;gap:6px;flex:1}
|
||||
.kb-tag{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--mid)}
|
||||
.kb-body h3{font-size:.98rem;font-weight:700;line-height:1.2}
|
||||
.kb-body p{font-size:.8rem;color:var(--muted);line-height:1.4;flex:1}
|
||||
.kb-meta{font-size:.74rem;color:var(--muted);display:flex;gap:12px;border-top:1px solid var(--line);padding-top:8px}
|
||||
|
||||
/* tech */
|
||||
.feat{display:flex;gap:14px;align-items:flex-start;background:var(--card);border:1px solid var(--line);
|
||||
border-radius:14px;padding:1.5vh 1.2vw}
|
||||
.feat .fic{width:42px;height:42px;border-radius:11px;background:rgba(16,185,129,.14);
|
||||
display:flex;align-items:center;justify-content:center;font-size:1.25rem;flex-shrink:0}
|
||||
.feat h3{font-size:1rem;font-weight:700;margin-bottom:4px}
|
||||
.feat p{font-size:.84rem;color:var(--muted);line-height:1.45}
|
||||
|
||||
/* pricing */
|
||||
.plan{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:2.4vh 1.5vw;
|
||||
display:flex;flex-direction:column;gap:1.2vh;position:relative}
|
||||
.plan.pop{border-color:var(--mid);background:linear-gradient(180deg,rgba(16,185,129,.07),var(--card))}
|
||||
.plan-badge{position:absolute;top:-12px;left:50%;transform:translateX(-50%);
|
||||
background:var(--mid);color:#04211a;font-size:.7rem;font-weight:800;text-transform:uppercase;
|
||||
letter-spacing:.06em;padding:4px 12px;border-radius:20px}
|
||||
.plan .pn{font-family:'Montserrat',sans-serif;font-weight:800;font-size:1.2rem}
|
||||
.plan .pp{font-family:'Montserrat',sans-serif;font-weight:800;font-size:2rem;letter-spacing:-.02em}
|
||||
.plan .pp small{font-size:.85rem;color:var(--muted);font-weight:500}
|
||||
.plan ul{list-style:none;display:flex;flex-direction:column;gap:9px;margin-top:.6vh}
|
||||
.plan li{font-size:.86rem;color:var(--muted);display:flex;gap:9px;align-items:flex-start}
|
||||
.plan li::before{content:'✓';color:var(--mid);font-weight:800}
|
||||
.roi-box{background:linear-gradient(135deg,var(--primary),var(--dark));border-radius:18px;
|
||||
padding:2.4vh 2vw;display:flex;align-items:center;gap:2vw;margin-top:3vh}
|
||||
.roi-box .rb-num{font-family:'Montserrat',sans-serif;font-weight:900;font-size:2.6rem;color:#fff;line-height:1}
|
||||
.roi-box .rb-txt{font-size:1.05rem;color:rgba(255,255,255,.85);line-height:1.4}
|
||||
|
||||
/* CTA */
|
||||
#s12{background:linear-gradient(150deg,#022C22 0%,#064E3B 45%,#047857 120%);text-align:center;align-items:center;justify-content:center}
|
||||
#s12 h1{font-family:'Montserrat',sans-serif;font-weight:800;font-size:clamp(2.2rem,4.6vw,4rem);color:#fff;margin-bottom:2vh;letter-spacing:-.02em}
|
||||
#s12 .cta-sub{font-size:clamp(1rem,1.6vw,1.4rem);color:rgba(255,255,255,.75);max-width:40ch;margin:0 auto 4vh}
|
||||
.cta-cards{display:flex;gap:1.4vw;justify-content:center;flex-wrap:wrap;margin-bottom:4vh}
|
||||
.cta-c{background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.18);border-radius:16px;
|
||||
padding:2.2vh 2vw;min-width:200px;backdrop-filter:blur(8px)}
|
||||
.cta-c .cc-n{font-family:'Montserrat',sans-serif;font-weight:800;font-size:1.7rem;color:#fff}
|
||||
.cta-c .cc-l{font-size:.9rem;color:rgba(255,255,255,.7);margin-top:6px}
|
||||
.cta-btn{display:inline-block;background:#fff;color:var(--dark);font-weight:800;font-size:1.05rem;
|
||||
padding:16px 40px;border-radius:14px;text-decoration:none}
|
||||
.cta-contact{margin-top:3vh;color:rgba(255,255,255,.7);font-size:.95rem}
|
||||
|
||||
/* ── Chrome: nav + progress ─────────────────────── */
|
||||
.chrome{position:fixed;left:0;right:0;bottom:0;z-index:50;display:flex;align-items:center;
|
||||
justify-content:space-between;padding:14px 28px;pointer-events:none}
|
||||
.nav-btns{display:flex;gap:10px;pointer-events:auto}
|
||||
.nav-btn{width:42px;height:42px;border-radius:11px;background:var(--card);border:1px solid var(--line-2);
|
||||
color:var(--text);font-size:1.1rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s}
|
||||
.nav-btn:hover{background:var(--primary);border-color:var(--primary)}
|
||||
.nav-btn:disabled{opacity:.3;cursor:default}
|
||||
.nav-btn:disabled:hover{background:var(--card);border-color:var(--line-2)}
|
||||
.counter{font-size:.85rem;color:var(--muted);font-weight:600;pointer-events:auto;
|
||||
background:var(--card);border:1px solid var(--line);padding:8px 14px;border-radius:11px}
|
||||
.counter b{color:var(--text)}
|
||||
.progress{position:fixed;left:0;bottom:0;height:3px;background:linear-gradient(90deg,var(--primary),var(--mid));
|
||||
z-index:60;transition:width .4s ease;border-radius:0 3px 3px 0}
|
||||
.topbar{position:fixed;top:0;left:0;right:0;z-index:40;display:flex;align-items:center;justify-content:space-between;
|
||||
padding:16px 28px;pointer-events:none}
|
||||
.topbar .tb-brand{font-family:'Montserrat',sans-serif;font-weight:800;font-size:.9rem;color:var(--muted);letter-spacing:-.01em}
|
||||
.topbar .tb-brand span{color:var(--mid)}
|
||||
.topbar .tb-page{font-size:.78rem;color:var(--muted);text-transform:uppercase;letter-spacing:.1em}
|
||||
|
||||
@media (max-width:900px){
|
||||
.grid-5,.grid-4{grid-template-columns:repeat(2,1fr)}
|
||||
.grid-3{grid-template-columns:1fr}
|
||||
.dash{grid-template-columns:1fr}
|
||||
.pipe{flex-wrap:wrap}
|
||||
.pstep{min-width:30%}.pstep::after{display:none}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="topbar">
|
||||
<div class="tb-brand">@<span>wasrusgen1</span> CRM</div>
|
||||
<div class="tb-page" id="pageLabel">Титул</div>
|
||||
</div>
|
||||
|
||||
<div class="deck" id="deck">
|
||||
|
||||
<!-- 1 ── COVER ───────────────────────────────────── -->
|
||||
<section class="slide active" data-label="Титул" id="s1">
|
||||
<div class="brand-row">
|
||||
<div class="at-badge">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="brand-stack">
|
||||
<div class="brand-handle">@wasrusgen1</div>
|
||||
<div class="brand-name">CRM</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kicker" style="color:rgba(255,255,255,.7)">CRM для мебельных салонов нового поколения</div>
|
||||
<h1>Управляйте мебельным бизнесом как единой системой</h1>
|
||||
<div class="cover-sub">Видеть бизнес в реальном времени. Управлять без потерь.</div>
|
||||
<div class="cover-meta">
|
||||
<div><b>Централизация</b><span>Вся сеть в одном окне</span></div>
|
||||
<div><b>Прозрачность</b><span>Данные без искажений</span></div>
|
||||
<div><b>Стандартизация</b><span>Единые процессы продаж</span></div>
|
||||
<div><b>Качество</b><span>Контроль сервиса на местах</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 2 ── PROBLEM ─────────────────────────────────── -->
|
||||
<section class="slide" data-label="Проблема" id="s2">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Проблема</div>
|
||||
<h2 class="title">Мебельный бизнес работает <span class="grad">вслепую</span></h2>
|
||||
<p class="lead">На рынке нет специализированной CRM для мебельных салонов. Каждый салон — отдельный остров данных.</p>
|
||||
</div>
|
||||
<div class="grid-4">
|
||||
<div class="card"><div class="ic red">📊</div><h3>Excel-хаос</h3><p>Заказы, замеры и клиенты — в десятках разрозненных таблиц. Версии расходятся, данные теряются.</p></div>
|
||||
<div class="card"><div class="ic red">🕶️</div><h3>Собственник слеп</h3><p>Нет единой картины. О проблемах узнаёшь постфактум — когда клиент уже ушёл.</p></div>
|
||||
<div class="card"><div class="ic red">🔀</div><h3>Данные искажаются</h3><p>Ручной ввод, переписывание, человеческий фактор. Цифры в отчётах не сходятся с реальностью.</p></div>
|
||||
<div class="card"><div class="ic red">🧩</div><h3>Разные системы</h3><p>Менеджер, замерщик и сборщик работают каждый в своём. Информация не передаётся по цепочке.</p></div>
|
||||
</div>
|
||||
<div class="mt">
|
||||
<div class="flow">
|
||||
<div class="flow-node bad">Салон №1<br><small style="color:var(--muted)">Excel</small></div>
|
||||
<span class="flow-arrow">✕</span>
|
||||
<div class="flow-node bad">Салон №2<br><small style="color:var(--muted)">Google Sheets</small></div>
|
||||
<span class="flow-arrow">✕</span>
|
||||
<div class="flow-node bad">Замерщики<br><small style="color:var(--muted)">блокнот / WhatsApp</small></div>
|
||||
<span class="flow-arrow">✕</span>
|
||||
<div class="flow-node bad">Собственник<br><small style="color:var(--muted)">не видит ничего</small></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 3 ── SOLUTION ────────────────────────────────── -->
|
||||
<section class="slide" data-label="Решение" id="s3">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Решение</div>
|
||||
<h2 class="title">Одна система — <span class="grad">весь бизнес как на ладони</span></h2>
|
||||
<p class="lead">Все салоны, сотрудники и заказы — в едином пространстве. Данные вводятся один раз и идут по цепочке без потерь.</p>
|
||||
</div>
|
||||
<div class="flow mt">
|
||||
<div class="flow-node good">Салон №1</div>
|
||||
<div class="flow-node good">Салон №2</div>
|
||||
<div class="flow-node good">Замерщики</div>
|
||||
<span class="flow-arrow" style="color:var(--mid)">→</span>
|
||||
<div class="flow-hub"><div class="h-t">@wasrusgen1 CRM</div><div class="h-s">единое облако данных</div></div>
|
||||
<span class="flow-arrow" style="color:var(--mid)">→</span>
|
||||
<div class="flow-node good">Собственник<br><small style="color:var(--mid)">видит всё в реальном времени</small></div>
|
||||
</div>
|
||||
<div class="grid-4 mt">
|
||||
<div class="card"><div class="ic green">🗂️</div><h3>Централизация</h3><p>Все данные в одном месте — никаких разрозненных таблиц.</p></div>
|
||||
<div class="card"><div class="ic green">🔎</div><h3>Прозрачность</h3><p>Собственник видит реальное состояние сети 24/7.</p></div>
|
||||
<div class="card"><div class="ic green">⚙️</div><h3>Стандартизация</h3><p>Единый процесс продаж и сервиса во всех салонах.</p></div>
|
||||
<div class="card"><div class="ic green">⭐</div><h3>Качество</h3><p>Контроль работы сотрудников и оценка клиентом.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 4 ── ROLES ───────────────────────────────────── -->
|
||||
<section class="slide" data-label="Роли" id="s4">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Кто использует систему</div>
|
||||
<h2 class="title">5 ролей — каждая видит <span class="grad">своё</span></h2>
|
||||
<p class="lead">Права доступа настроены под задачу. Сотрудник видит только то, что нужно для работы.</p>
|
||||
</div>
|
||||
<div class="grid-5">
|
||||
<div class="role-card"><div class="role-ic">👑</div><h3>КД / Собственник</h3><div class="zone">Вся сеть</div><p>Выручка, рейтинги, план/факт по всем салонам.</p></div>
|
||||
<div class="role-card"><div class="role-ic">🏢</div><h3>Директор салона</h3><div class="zone">Свой салон</div><p>Команда, заказы и показатели одной точки.</p></div>
|
||||
<div class="role-card"><div class="role-ic">🧑💼</div><h3>Менеджер</h3><div class="zone">Заказы и клиенты</div><p>Ведёт сделки от лида до закрытия, записи.</p></div>
|
||||
<div class="role-card"><div class="role-ic">📐</div><h3>Замерщик</h3><div class="zone">Задания на замер</div><p>Получает задачи, GPS-чекин, отметка о выполнении.</p></div>
|
||||
<div class="role-card"><div class="role-ic">🔧</div><h3>Сборщик</h3><div class="zone">Монтаж</div><p>Сборка, фото чистоты, оценки качества работ.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 5 ── PIPELINE ────────────────────────────────── -->
|
||||
<section class="slide" data-label="Воронка заказа" id="s5">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Воронка заказа</div>
|
||||
<h2 class="title">Путь заказа — <span class="grad">8 контролируемых этапов</span></h2>
|
||||
<p class="lead">Каждый этап фиксируется в системе. Видно, где заказ «застрял», и кто за него отвечает.</p>
|
||||
</div>
|
||||
<div class="pipe mt">
|
||||
<div class="pstep"><span class="pn">01</span><span class="pic">🎯</span><span class="pl">Лид</span></div>
|
||||
<div class="pstep"><span class="pn">02</span><span class="pic">📐</span><span class="pl">Замер</span></div>
|
||||
<div class="pstep"><span class="pn">03</span><span class="pic">✏️</span><span class="pl">Проект</span></div>
|
||||
<div class="pstep"><span class="pn">04</span><span class="pic">🔌</span><span class="pl">Техника</span></div>
|
||||
<div class="pstep hl"><span class="pn">05</span><span class="pic">🧮</span><span class="pl">Технолог</span></div>
|
||||
<div class="pstep"><span class="pn">06</span><span class="pic">🏭</span><span class="pl">Производство</span></div>
|
||||
<div class="pstep"><span class="pn">07</span><span class="pic">🔧</span><span class="pl">Сборка</span></div>
|
||||
<div class="pstep"><span class="pn">08</span><span class="pic">✅</span><span class="pl">Закрыт</span></div>
|
||||
</div>
|
||||
<div class="grid-3 mt">
|
||||
<div class="card"><div class="ic green">⏱️</div><h3>Сроки под контролем</h3><p>Система подсвечивает заказы, которые задерживаются на этапе дольше нормы.</p></div>
|
||||
<div class="card"><div class="ic green">👤</div><h3>Ответственный на каждом шаге</h3><p>Понятно, кто двигает заказ дальше и где зона ответственности.</p></div>
|
||||
<div class="card"><div class="ic green">🔔</div><h3>Автоуведомления</h3><p>Менеджер и клиент получают статус автоматически — без ручных звонков.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 6 ── OWNER DASHBOARD ─────────────────────────── -->
|
||||
<section class="slide" data-label="Дашборд собственника" id="s6">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Дашборд собственника</div>
|
||||
<h2 class="title">Что видит КД <span class="grad">каждое утро</span></h2>
|
||||
</div>
|
||||
<div class="dash">
|
||||
<div class="panel">
|
||||
<div class="panel-h">Выручка по салонам · месяц</div>
|
||||
<div class="bar-row"><span class="bl">Москва-Север</span><div class="bar-track"><div class="bar-fill" style="width:92%"></div></div><span class="bv">4.6М</span></div>
|
||||
<div class="bar-row"><span class="bl">Москва-Юг</span><div class="bar-track"><div class="bar-fill" style="width:74%"></div></div><span class="bv">3.7М</span></div>
|
||||
<div class="bar-row"><span class="bl">СПб-Центр</span><div class="bar-track"><div class="bar-fill" style="width:68%"></div></div><span class="bv">3.4М</span></div>
|
||||
<div class="bar-row"><span class="bl">Казань</span><div class="bar-track"><div class="bar-fill" style="width:51%"></div></div><span class="bv">2.5М</span></div>
|
||||
<div class="bar-row"><span class="bl">Краснодар</span><div class="bar-track"><div class="bar-fill" style="width:44%"></div></div><span class="bv">2.2М</span></div>
|
||||
<div class="panel-h" style="margin-top:2vh">Просроченные заказы</div>
|
||||
<table>
|
||||
<tr><th>Заказ</th><th>Салон</th><th>Этап</th><th>Статус</th></tr>
|
||||
<tr><td>#4821</td><td>Казань</td><td>Производство</td><td><span class="pill late">+6 дн</span></td></tr>
|
||||
<tr><td>#4790</td><td>СПб-Центр</td><td>Сборка</td><td><span class="pill warn">+2 дн</span></td></tr>
|
||||
<tr><td>#4763</td><td>Москва-Юг</td><td>Замер</td><td><span class="pill late">+4 дн</span></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<div class="kpi-row">
|
||||
<div class="kpi"><div class="v">16.4М ₽</div><div class="l">Выручка сети</div><div class="d up">▲ 12% к плану</div></div>
|
||||
<div class="kpi"><div class="v">87%</div><div class="l">План / факт</div><div class="d up">▲ 5 п.п.</div></div>
|
||||
<div class="kpi"><div class="v">142</div><div class="l">Заказов в работе</div><div class="d down">▼ 3 просрочены</div></div>
|
||||
<div class="kpi"><div class="v">4.8<small>/5</small></div><div class="l">Средний рейтинг</div><div class="d up">▲ 0.2</div></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel-h">Топ сотрудников по рейтингу</div>
|
||||
<table>
|
||||
<tr><th>Сотрудник</th><th>Роль</th><th>Балл</th></tr>
|
||||
<tr><td>А. Орлова</td><td>Менеджер</td><td><span class="pill ok">9.6</span></td></tr>
|
||||
<tr><td>Р. Васильев</td><td>Замерщик</td><td><span class="pill ok">9.4</span></td></tr>
|
||||
<tr><td>Д. Соколов</td><td>Сборщик</td><td><span class="pill ok">9.1</span></td></tr>
|
||||
<tr><td>М. Лебедь</td><td>Менеджер</td><td><span class="pill warn">7.2</span></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 7 ── MANAGERS ────────────────────────────────── -->
|
||||
<section class="slide" data-label="Управление менеджерами" id="s7">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Управление менеджерами</div>
|
||||
<h2 class="title">Шахматка записей и <span class="grad">контроль на местах</span></h2>
|
||||
</div>
|
||||
<div class="dash">
|
||||
<div class="panel">
|
||||
<div class="panel-h">Шахматка записей · сегодня</div>
|
||||
<table class="sched">
|
||||
<tr><th>Время</th><th>Орлова</th><th>Лебедь</th><th>Замер</th></tr>
|
||||
<tr><td>10:00</td><td class="cell-busy">Клиент К.</td><td class="cell-free">—</td><td class="cell-gps">📍 GPS</td></tr>
|
||||
<tr><td>12:00</td><td class="cell-busy">Клиент М.</td><td class="cell-busy">Клиент П.</td><td class="cell-free">—</td></tr>
|
||||
<tr><td>14:00</td><td class="cell-free">—</td><td class="cell-busy">Клиент С.</td><td class="cell-gps">📍 GPS</td></tr>
|
||||
<tr><td>16:00</td><td class="cell-busy">Клиент В.</td><td class="cell-free">—</td><td class="cell-busy">Замер №4821</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<div class="grid-2" style="gap:1vw">
|
||||
<div class="card"><div class="ic green">🗓️</div><h3>Расписание</h3><p>Все встречи и замеры команды — в одном календаре.</p></div>
|
||||
<div class="card"><div class="ic green">📍</div><h3>GPS-чекин</h3><p>Замерщик отмечается на адресе — факт выезда подтверждён.</p></div>
|
||||
<div class="card"><div class="ic green">📨</div><h3>Заявки</h3><p>Лиды распределяются по менеджерам автоматически.</p></div>
|
||||
<div class="card"><div class="ic green">📈</div><h3>Нагрузка</h3><p>Видно, кто перегружен, а у кого есть свободные слоты.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 8 ── RATING ──────────────────────────────────── -->
|
||||
<section class="slide" data-label="Рейтинговая система" id="s8">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Рейтинговая система</div>
|
||||
<h2 class="title">Оценки <span class="grad">влияют на премию</span></h2>
|
||||
<p class="lead">Сотрудников оценивают коллеги по цепочке и клиент. Оценки анонимны — честность вместо страха.</p>
|
||||
</div>
|
||||
<div class="grid-3">
|
||||
<div class="rate-card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start">
|
||||
<div><div style="color:var(--muted);font-size:.85rem">Менеджер</div><div class="scorebig">9.6<small>/10</small></div></div>
|
||||
<div class="stars">★★★★★</div>
|
||||
</div>
|
||||
<div class="crit"><span class="cn">Скорость ответа</span><span class="cs up">9.8</span></div>
|
||||
<div class="crit"><span class="cn">Точность проекта</span><span class="cs up">9.5</span></div>
|
||||
<div class="crit"><span class="cn">Оценка клиента</span><span class="cs up">9.4</span></div>
|
||||
</div>
|
||||
<div class="rate-card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start">
|
||||
<div><div style="color:var(--muted);font-size:.85rem">Замерщик</div><div class="scorebig">9.4<small>/10</small></div></div>
|
||||
<div class="stars">★★★★★</div>
|
||||
</div>
|
||||
<div class="crit"><span class="cn">Точность замера</span><span class="cs up">9.7</span></div>
|
||||
<div class="crit"><span class="cn">Пунктуальность</span><span class="cs up">9.2</span></div>
|
||||
<div class="crit"><span class="cn">Оценка клиента</span><span class="cs up">9.3</span></div>
|
||||
</div>
|
||||
<div class="rate-card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start">
|
||||
<div><div style="color:var(--muted);font-size:.85rem">Сборщик</div><div class="scorebig">9.1<small>/10</small></div></div>
|
||||
<div class="stars">★★★★☆</div>
|
||||
</div>
|
||||
<div class="crit"><span class="cn">Качество монтажа</span><span class="cs up">9.3</span></div>
|
||||
<div class="crit"><span class="cn">Чистота после работ</span><span class="cs up">8.8</span></div>
|
||||
<div class="crit"><span class="cn">Оценка клиента</span><span class="cs up">9.2</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-3 mt">
|
||||
<div class="card"><div class="ic green">🤝</div><h3>Оценка по цепочке</h3><p>Сотрудники оценивают друг друга — критерии под каждую роль.</p></div>
|
||||
<div class="card"><div class="ic green">🕵️</div><h3>Анонимность</h3><p>Кто поставил оценку — не видно. Объективная обратная связь.</p></div>
|
||||
<div class="card"><div class="ic green">💰</div><h3>Влияние на премию</h3><p>Рейтинг автоматически учитывается при расчёте бонусов.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 9 ── KNOWLEDGE BASE ──────────────────────────── -->
|
||||
<section class="slide" data-label="База знаний" id="s9">
|
||||
<div class="head-block">
|
||||
<div class="kicker">База знаний</div>
|
||||
<h2 class="title">Экспертиза <span class="grad">встроена в систему</span></h2>
|
||||
<p class="lead">Готовые статьи помогают менеджеру с низкими компетенциями работать на уровне эксперта — и отправлять материалы клиенту в один клик.</p>
|
||||
</div>
|
||||
<div class="grid-4">
|
||||
<div class="kb-card"><div class="kb-cover">📏</div><div class="kb-body"><div class="kb-tag">Эргономика</div><h3>Эргономика кухонного помещения</h3><p>Рабочий треугольник, высоты, расстояния между зонами.</p><div class="kb-meta"><span>📖 6 мин</span><span>📤 Клиенту</span></div></div></div>
|
||||
<div class="kb-card"><div class="kb-cover">🔌</div><div class="kb-body"><div class="kb-tag">Техника</div><h3>Подбор встраиваемой техники</h3><p>Как подобрать технику под габариты и бюджет клиента.</p><div class="kb-meta"><span>📖 8 мин</span><span>📤 Клиенту</span></div></div></div>
|
||||
<div class="kb-card"><div class="kb-cover">🚪</div><div class="kb-body"><div class="kb-tag">Хранение</div><h3>Системы хранения и наполнение</h3><p>Фурнитура, выдвижные механизмы, организация шкафов.</p><div class="kb-meta"><span>📖 5 мин</span><span>📤 Клиенту</span></div></div></div>
|
||||
<div class="kb-card"><div class="kb-cover">💡</div><div class="kb-body"><div class="kb-tag">Свет</div><h3>Освещение рабочих зон</h3><p>Подсветка столешницы, сценарии света, температура.</p><div class="kb-meta"><span>📖 4 мин</span><span>📤 Клиенту</span></div></div></div>
|
||||
</div>
|
||||
<div class="grid-3 mt">
|
||||
<div class="card"><div class="ic green">🎓</div><h3>Рост компетенций</h3><p>Новый сотрудник быстро выходит на качество продаж.</p></div>
|
||||
<div class="card"><div class="ic green">📤</div><h3>Отправка клиенту</h3><p>Статья уходит клиенту прямо из карточки сделки.</p></div>
|
||||
<div class="card"><div class="ic green">✍️</div><h3>Свои материалы</h3><p>Собственник добавляет регламенты и стандарты сети.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 10 ── TECH ───────────────────────────────────── -->
|
||||
<section class="slide" data-label="Технические детали" id="s10">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Технические детали</div>
|
||||
<h2 class="title">Современная <span class="grad">облачная архитектура</span></h2>
|
||||
<p class="lead">Ничего не нужно устанавливать. Работает в браузере и на телефоне, подключение — за один день.</p>
|
||||
</div>
|
||||
<div class="grid-2">
|
||||
<div class="feat"><div class="fic">📱</div><div><h3>Мобильная CRM</h3><p>Замерщики и сборщики работают с телефона прямо на объекте.</p></div></div>
|
||||
<div class="feat"><div class="fic">🌐</div><div><h3>Работает в браузере</h3><p>Никаких установок и серверов в салоне — открыл и работаешь.</p></div></div>
|
||||
<div class="feat"><div class="fic">⚡</div><div><h3>Подключение за 1 день</h3><p>Заводим салоны, роли и сотрудников — и сразу в работу.</p></div></div>
|
||||
<div class="feat"><div class="fic">☁️</div><div><h3>Данные в облаке</h3><p>Резервное копирование, доступ из любой точки, ничего не теряется.</p></div></div>
|
||||
<div class="feat"><div class="fic">🔒</div><div><h3>Разграничение доступа</h3><p>Каждая роль видит только свою зону. Данные под защитой.</p></div></div>
|
||||
<div class="feat"><div class="fic">🛟</div><div><h3>Поддержка 24/7</h3><p>Помогаем на старте и сопровождаем на всём пути.</p></div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 11 ── PRICING / ROI ──────────────────────────── -->
|
||||
<section class="slide" data-label="Стоимость и ROI" id="s11">
|
||||
<div class="head-block">
|
||||
<div class="kicker">Стоимость и ROI</div>
|
||||
<h2 class="title">Тарифы под <span class="grad">размер бизнеса</span></h2>
|
||||
</div>
|
||||
<div class="grid-3">
|
||||
<div class="plan">
|
||||
<div class="pn">Старт</div>
|
||||
<div class="pp">— ₽<small>/мес</small></div>
|
||||
<ul><li>1 салон</li><li>до 5 пользователей</li><li>Воронка и заказы</li><li>База знаний</li></ul>
|
||||
</div>
|
||||
<div class="plan pop">
|
||||
<div class="plan-badge">Популярный</div>
|
||||
<div class="pn">Бизнес</div>
|
||||
<div class="pp">— ₽<small>/мес</small></div>
|
||||
<ul><li>до 3 салонов</li><li>до 25 пользователей</li><li>Рейтинговая система</li><li>GPS-чекин и шахматка</li><li>Дашборд собственника</li></ul>
|
||||
</div>
|
||||
<div class="plan">
|
||||
<div class="pn">Сеть</div>
|
||||
<div class="pp">— ₽<small>/мес</small></div>
|
||||
<ul><li>Неограниченно салонов</li><li>Неограниченно пользователей</li><li>Все функции Бизнес</li><li>Персональный менеджер</li><li>Кастомизация процессов</li></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="roi-box">
|
||||
<div class="rb-num">×3</div>
|
||||
<div class="rb-txt"><b>1 удержанный клиент = 3 месяца подписки.</b> Система окупается, если помогает не потерять хотя бы одну сделку в квартал.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 12 ── CTA ────────────────────────────────────── -->
|
||||
<section class="slide" data-label="Следующий шаг" id="s12">
|
||||
<div class="kicker" style="color:rgba(255,255,255,.7);justify-content:center">Следующий шаг</div>
|
||||
<h1>Посмотрите систему на своём бизнесе</h1>
|
||||
<div class="cta-sub">Покажем дашборд на ваших салонах и настроим под ваши процессы.</div>
|
||||
<div class="cta-cards">
|
||||
<div class="cta-c"><div class="cc-n">30 минут</div><div class="cc-l">Демо системы онлайн</div></div>
|
||||
<div class="cta-c"><div class="cc-n">3 дня</div><div class="cc-l">Полное внедрение в сети</div></div>
|
||||
<div class="cta-c"><div class="cc-n">Бесплатно</div><div class="cc-l">Первый месяц работы</div></div>
|
||||
</div>
|
||||
<a class="cta-btn" href="#">Записаться на демо →</a>
|
||||
<div class="cta-contact">@wasrusgen1 · i@wasrusgen.ru</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- chrome -->
|
||||
<div class="progress" id="progress"></div>
|
||||
<div class="chrome">
|
||||
<div class="counter">Слайд <b id="cur">1</b> / <b id="total">12</b></div>
|
||||
<div class="nav-btns">
|
||||
<button class="nav-btn" id="prev" aria-label="Назад">←</button>
|
||||
<button class="nav-btn" id="next" aria-label="Вперёд">→</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const slides=[...document.querySelectorAll('.slide')];
|
||||
const total=slides.length;
|
||||
let i=0;
|
||||
const elCur=document.getElementById('cur');
|
||||
const elTotal=document.getElementById('total');
|
||||
const elProg=document.getElementById('progress');
|
||||
const elPrev=document.getElementById('prev');
|
||||
const elNext=document.getElementById('next');
|
||||
const elLabel=document.getElementById('pageLabel');
|
||||
elTotal.textContent=total;
|
||||
|
||||
function show(n){
|
||||
i=Math.max(0,Math.min(total-1,n));
|
||||
slides.forEach((s,k)=>s.classList.toggle('active',k===i));
|
||||
elCur.textContent=i+1;
|
||||
elProg.style.width=((i+1)/total*100)+'%';
|
||||
elPrev.disabled=i===0;
|
||||
elNext.disabled=i===total-1;
|
||||
elLabel.textContent=slides[i].dataset.label||'';
|
||||
}
|
||||
function nextS(){show(i+1)}
|
||||
function prevS(){show(i-1)}
|
||||
|
||||
elNext.addEventListener('click',nextS);
|
||||
elPrev.addEventListener('click',prevS);
|
||||
document.addEventListener('keydown',e=>{
|
||||
if(e.key==='ArrowRight'||e.key==='PageDown'||e.key===' '){e.preventDefault();nextS();}
|
||||
else if(e.key==='ArrowLeft'||e.key==='PageUp'){e.preventDefault();prevS();}
|
||||
else if(e.key==='Home'){show(0);}
|
||||
else if(e.key==='End'){show(total-1);}
|
||||
});
|
||||
// swipe
|
||||
let x0=null;
|
||||
document.addEventListener('touchstart',e=>x0=e.touches[0].clientX,{passive:true});
|
||||
document.addEventListener('touchend',e=>{
|
||||
if(x0===null)return;
|
||||
const dx=e.changedTouches[0].clientX-x0;
|
||||
if(Math.abs(dx)>50){dx<0?nextS():prevS();}
|
||||
x0=null;
|
||||
},{passive:true});
|
||||
|
||||
show(0);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
33
Mokap/tokens.css
Normal file
33
Mokap/tokens.css
Normal file
@ -0,0 +1,33 @@
|
||||
/* ============================================================
|
||||
ЗАЩИТА — ЕДИНЫЙ ИСТОЧНИК ТОКЕНОВ (цвет + шрифт).
|
||||
Все экраны и брендбук берут значения ТОЛЬКО отсюда.
|
||||
Никаких локальных hex и сторонних шрифтов — без исключений.
|
||||
============================================================ */
|
||||
:root{
|
||||
/* — шрифты — */
|
||||
--font-ui: 'Inter', system-ui, sans-serif; /* весь интерфейс */
|
||||
--font-logo: 'Montserrat', sans-serif; /* только логотип */
|
||||
|
||||
/* — бренд (Burgundy) — */
|
||||
--bg: #9F1239; /* primary: кнопки, акцент, лого */
|
||||
--bghv: #7A0E2E; /* hover primary (Burgundy 900) */
|
||||
--dark: #3B0212; /* тёмные плашки, градиенты */
|
||||
--tint: #F0E3E6; /* бордо-подложка */
|
||||
--shell: #20181A; /* тёмная оболочка: топбар/сайдбар/тосты */
|
||||
--shell2: #2C1820; /* оболочка — второй стоп градиента */
|
||||
|
||||
/* — нейтральная шкала — */
|
||||
--paper: #F9FAFB; /* полотно (текст договора) */
|
||||
--surf: #F3F4F6; /* фон рабочей области */
|
||||
--card: #FFFFFF; /* карточки */
|
||||
--line: #E5E7EB; /* границы */
|
||||
--ink: #111827; /* основной текст */
|
||||
--mut: #6B7280; /* вторичный текст */
|
||||
--slate: #374151; /* заголовки */
|
||||
--slate2: #4B5563; /* приглушённые подписи */
|
||||
|
||||
/* — приглушённая (muted) семантика — */
|
||||
--ok: #4F856F; --okbg: #E8EFEA;
|
||||
--warn: #A87E3C; --warnbg: #F2EBDD; --warnhv: #EBDFC4;
|
||||
--dng: #A14C5A; --dngbg: #F0E3E6; --dnghv: #E4CDD3;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user