wasrusgen1-crm/docs/elena_live.html

182 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Елена — живой кабинет · @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{--primary:#047857;--dark:#064E3B;--mid:#10B981;--light:#ECFDF5;--bg:#F5F6F8;--white:#fff;--border:#E5E7EB;--text:#1A1A2E;--muted:#6B7280}
html,body{height:100%}
body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);display:flex;flex-direction:column;height:100vh}
.hdr{height:56px;background:var(--dark);display:flex;align-items:center;padding:0 20px;gap:12px;flex-shrink:0}
.hdr-ic{width:32px;height:32px;background:var(--primary);border-radius:8px;display:flex;align-items:center;justify-content:center;font-family:'Montserrat';font-weight:800;color:#fff;font-size:17px}
.hdr-t{font-family:'Montserrat';font-weight:700;font-size:15px;color:#fff}
.hdr-t em{color:rgba(255,255,255,.4);font-style:normal;margin:0 5px}
.hdr-elena{margin-left:auto;display:flex;align-items:center;gap:8px;color:rgba(255,255,255,.85);font-size:13px;font-weight:600}
.hdr-dot{width:8px;height:8px;border-radius:50%;background:var(--mid);box-shadow:0 0 6px var(--mid)}
.wrap{flex:1;display:flex;overflow:hidden}
.chat-col{flex:1;display:flex;flex-direction:column;min-width:0;border-right:1px solid var(--border)}
.chat{flex:1;overflow-y:auto;padding:24px;display:flex;flex-direction:column;gap:14px}
.msg{display:flex;gap:10px;max-width:80%}
.msg.user{align-self:flex-end;flex-direction:row-reverse}
.av{width:32px;height:32px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-family:'Montserrat';font-weight:800;font-size:13px;color:#fff}
.av.elena{background:var(--primary)}
.av.user{background:#6366F1}
.bubble{padding:11px 15px;border-radius:14px;font-size:14px;line-height:1.55;white-space:pre-wrap}
.bubble.elena{background:var(--white);border:1px solid var(--border);border-top-left-radius:4px}
.bubble.user{background:var(--primary);color:#fff;border-top-right-radius:4px}
.bubble strong{font-weight:700}
.typing{display:flex;gap:4px;padding:14px 16px;background:var(--white);border:1px solid var(--border);border-radius:14px;border-top-left-radius:4px;width:fit-content}
.typing span{width:7px;height:7px;border-radius:50%;background:#cbd5e1;animation:b 1.2s infinite}
.typing span:nth-child(2){animation-delay:.2s}.typing span:nth-child(3){animation-delay:.4s}
@keyframes b{0%,80%,100%{transform:scale(.7);opacity:.4}40%{transform:scale(1);opacity:1}}
.inbar{padding:14px 20px;border-top:1px solid var(--border);background:var(--white);display:flex;gap:10px;align-items:flex-end;flex-shrink:0}
.inp{flex:1;border:1.5px solid var(--border);border-radius:12px;padding:11px 15px;font-size:14px;font-family:'Inter';resize:none;outline:none;max-height:120px;line-height:1.5}
.inp:focus{border-color:var(--mid);box-shadow:0 0 0 3px rgba(16,185,129,.1)}
.send{width:44px;height:44px;border-radius:11px;background:var(--primary);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.send:hover{background:var(--dark)}.send:disabled{opacity:.4;cursor:default}
.build-btn{margin:0 20px 14px;padding:13px;border-radius:12px;background:linear-gradient(135deg,var(--dark),var(--primary));color:#fff;border:none;cursor:pointer;font-family:'Inter';font-weight:700;font-size:14px;flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:8px}
.build-btn:hover{opacity:.92}.build-btn:disabled{opacity:.5;cursor:default}
.model-col{width:42%;max-width:560px;overflow-y:auto;padding:24px;background:#fafbfc;display:none}
.model-col.show{display:block}
.mc-head{font-family:'Montserrat';font-weight:800;font-size:18px;color:var(--dark);margin-bottom:6px}
.mc-sum{font-size:13px;color:var(--muted);line-height:1.5;margin-bottom:18px;padding-bottom:18px;border-bottom:1px solid var(--border)}
.blk{background:var(--white);border:1px solid var(--border);border-radius:13px;padding:16px;margin-bottom:12px}
.blk-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
.blk-title{font-size:14px;font-weight:700;color:var(--text)}
.blk-pct{font-size:11px;font-weight:700;padding:2px 8px;border-radius:10px}
.blk-row{margin-bottom:9px}
.blk-lbl{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#9ca3af;margin-bottom:3px}
.blk-asis{font-size:13px;color:#374151;line-height:1.5}
.blk-pain{font-size:12px;color:#92400e;line-height:1.45;padding-left:14px;position:relative;margin-bottom:3px}
.blk-pain::before{content:'•';position:absolute;left:3px;color:#f59e0b}
.blk-tobe{font-size:13px;color:var(--primary);line-height:1.5;background:var(--light);padding:8px 10px;border-radius:8px}
.empty{text-align:center;color:#cbd5e1;padding:60px 20px;font-size:14px}
@media(max-width:860px){.model-col{position:fixed;inset:56px 0 0 0;width:100%;max-width:none;z-index:50}.chat-col{border:none}}
</style>
</head>
<body>
<div class="hdr">
<div class="hdr-ic">@</div>
<div class="hdr-t">wasrusgen1 <em>/</em> КОНСАЛТИНГ</div>
<div class="hdr-elena"><span class="hdr-dot"></span>Елена · онлайн</div>
</div>
<div class="wrap">
<div class="chat-col">
<div class="chat" id="chat"></div>
<button class="build-btn" id="buildBtn" onclick="buildModel()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
Построить мою бизнес-модель →
</button>
<div class="inbar">
<textarea class="inp" id="inp" rows="1" placeholder="Напишите Елене..." onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendMsg()}"></textarea>
<button class="send" id="sendBtn" onclick="sendMsg()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
</button>
</div>
</div>
<div class="model-col" id="modelCol">
<div class="empty">Бизнес-модель появится здесь после анализа</div>
</div>
</div>
<script>
const API = "https://claude-83-172-150-111.sslip.io/elena";
let token = localStorage.getItem("elena_token");
const chat = document.getElementById("chat");
const inp = document.getElementById("inp");
function esc(s){return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}
function fmt(s){return esc(s).replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>")}
function addMsg(role, text){
const m = document.createElement("div");
m.className = "msg " + (role==="user"?"user":"elena");
m.innerHTML = `<div class="av ${role==='user'?'user':'elena'}">${role==='user'?'Я':'Е'}</div><div class="bubble ${role==='user'?'user':'elena'}">${fmt(text)}</div>`;
chat.appendChild(m);
chat.scrollTop = chat.scrollHeight;
}
function showTyping(){
const t = document.createElement("div");
t.className="msg elena";t.id="typing";
t.innerHTML=`<div class="av elena">Е</div><div class="typing"><span></span><span></span><span></span></div>`;
chat.appendChild(t);chat.scrollTop=chat.scrollHeight;
}
function hideTyping(){const t=document.getElementById("typing");if(t)t.remove()}
async function init(){
if(token){
const r = await fetch(`${API}/api/project/${token}`);
if(r.ok){
const d = await r.json();
d.messages.forEach(m=>addMsg(m.role==="user"?"user":"elena", m.content));
if(d.model) renderModel(d.model);
return;
}
}
const r = await fetch(`${API}/api/project/new`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})});
const d = await r.json();
token = d.token; localStorage.setItem("elena_token", token);
addMsg("elena", d.greeting);
}
async function sendMsg(){
const text = inp.value.trim();
if(!text) return;
inp.value=""; inp.style.height="auto";
addMsg("user", text);
document.getElementById("sendBtn").disabled=true;
showTyping();
try{
const r = await fetch(`${API}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token,message:text})});
const d = await r.json();
hideTyping();
addMsg("elena", d.reply || ("Ошибка: "+(d.error||"нет ответа")));
}catch(e){hideTyping();addMsg("elena","Ошибка соединения: "+e.message)}
document.getElementById("sendBtn").disabled=false;
}
const COLORS={product:"#6366F1",customers:"#3B82F6",partners:"#8B5CF6",process:"#047857",team:"#EC4899",tools:"#0EA5E9",money:"#F59E0B"};
function pctColor(p){return p>=70?"#047857":p>=45?"#F59E0B":"#EF4444"}
function pctBg(p){return p>=70?"#ECFDF5":p>=45?"#FEF3C7":"#FEF2F2"}
function renderModel(m){
const col = document.getElementById("modelCol");
col.classList.add("show");
let html = `<div class="mc-head">Ваша бизнес-модель</div><div class="mc-sum">${esc(m.client_summary)}</div>`;
m.blocks.forEach(b=>{
html += `<div class="blk">
<div class="blk-top"><div class="blk-title">${esc(b.title)}</div>
<div class="blk-pct" style="color:${pctColor(b.completeness)};background:${pctBg(b.completeness)}">${b.completeness}%</div></div>
<div class="blk-row"><div class="blk-lbl">Как есть</div><div class="blk-asis">${esc(b.as_is)}</div></div>`;
if(b.pains&&b.pains.length){html+=`<div class="blk-row"><div class="blk-lbl">Боли</div>`;b.pains.forEach(p=>html+=`<div class="blk-pain">${esc(p)}</div>`);html+=`</div>`}
html += `<div class="blk-row"><div class="blk-lbl">Как должно быть</div><div class="blk-tobe">${esc(b.to_be)}</div></div></div>`;
});
if(m.missing_info&&m.missing_info.length){
html+=`<div class="blk" style="border-color:#FDE68A;background:#FFFBEB"><div class="blk-title" style="margin-bottom:8px">Елена уточнит ещё</div>`;
m.missing_info.forEach(q=>html+=`<div class="blk-pain">${esc(q)}</div>`);html+=`</div>`;
}
col.innerHTML = html;
}
async function buildModel(){
const btn = document.getElementById("buildBtn");
btn.disabled=true; btn.innerHTML="Елена анализирует ваш бизнес... ⏳";
try{
const r = await fetch(`${API}/api/build-model`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token})});
const d = await r.json();
if(d.model){renderModel(d.model);btn.innerHTML="✓ Модель построена · обновить";}
else{btn.innerHTML="Ошибка: "+(d.error||"?");}
}catch(e){btn.innerHTML="Ошибка: "+e.message}
btn.disabled=false;
}
inp.addEventListener("input",()=>{inp.style.height="auto";inp.style.height=Math.min(inp.scrollHeight,120)+"px"});
init();
</script>
</body>
</html>