diff --git a/mockup.html b/mockup.html
index f1c9017..c8d2559 100644
--- a/mockup.html
+++ b/mockup.html
@@ -2554,13 +2554,246 @@ function startScan() {
setTimeout(() => { lbl.textContent = SCAN_PHRASES[i]; lbl.style.opacity = '1'; }, 180);
}, 800);
+ // Параллельно запускаем реальный API (если доступен)
+ var _apiResult = null;
+ if (_apiAvailable && text) {
+ fetch(API_BASE + '/api/deadlines', {
+ method: 'POST',
+ headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({text: text})
+ })
+ .then(function(r){ return r.json(); })
+ .then(function(data){
+ _apiResult = data;
+ // Сохраняем сроки для контекста Елены
+ if (data.deadlines && data.deadlines.length) {
+ _contractDeadlines = data.deadlines;
+ // Обновляем _DEADLINES для экрана "Сроки"
+ _DEADLINES = data.deadlines.map(function(d, idx){
+ return {
+ id: idx + 100,
+ caseId: 'case-new',
+ caseName: (data.meta && data.meta.type) || 'Договор',
+ title: d.title,
+ type: d.type || 'Другое',
+ date: d.date,
+ quote: d.quote || '',
+ done: false
+ };
+ });
+ }
+ })
+ .catch(function(e){ console.warn('API /deadlines:', e); });
+ }
+
setTimeout(() => {
clearInterval(interval);
lbl.style.opacity = '0';
- setTimeout(() => { showResults(ctypeKey); }, 200);
+ setTimeout(() => {
+ showResults(ctypeKey);
+ // Если API вернул need_signed_date — Елена спрашивает дату
+ if (_apiResult && _apiResult.need_signed_date) {
+ _askSignedDate();
+ }
+ // Если complexity_score >= 3 — предлагаем Council
+ if (_apiResult && _apiResult.council_trigger) {
+ setTimeout(function(){ _offerCouncil(_apiResult); }, 1500);
+ }
+ }, 200);
}, 4000);
}
+function _askSignedDate() {
+ /* Елена спрашивает дату подписания после сканирования */
+ var wrap = document.querySelector('.chatwrap');
+ if (!wrap) return;
+ var div = document.createElement('div');
+ div.id = 'signed-date-ask';
+ div.innerHTML =
+ '
' +
+ '
Елена
' +
+ 'Договор уже подписан? Укажите дату — буду считать живые сроки и покажу что горит прямо сейчас.
' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '
';
+ wrap.appendChild(div);
+ div.scrollIntoView({behavior:'smooth'});
+}
+
+function _setSignedDate(mode) {
+ var today = new Date();
+ var d;
+ if (mode === 'today') {
+ d = today.toISOString().slice(0,10);
+ } else if (mode === 'yesterday') {
+ today.setDate(today.getDate() - 1);
+ d = today.toISOString().slice(0,10);
+ } else {
+ d = (document.getElementById('signed-date-inp') || {}).value;
+ if (!d) { toast('Выберите дату'); return; }
+ }
+ // Убираем вопрос
+ var ask = document.getElementById('signed-date-ask');
+ if (ask) ask.remove();
+
+ // Перезапрашиваем deadlines с датой
+ var text = (document.getElementById('el-paste').value || '').trim();
+ if (!text || !_apiAvailable) { toast('📅 Дата ' + d + ' сохранена'); return; }
+
+ fetch(API_BASE + '/api/deadlines', {
+ method: 'POST',
+ headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({text: text, signed_date: d})
+ })
+ .then(function(r){ return r.json(); })
+ .then(function(data){
+ if (data.deadlines) {
+ _contractDeadlines = data.deadlines;
+ _DEADLINES = data.deadlines.map(function(dl, idx){
+ return {id:idx+100, caseId:'case-new', caseName:(data.meta&&data.meta.type)||'Договор',
+ title:dl.title, type:dl.type||'Другое', date:dl.date, quote:dl.quote||'', done:false};
+ });
+ // Показываем что горит
+ var hot = data.deadlines.filter(function(dl){ return dl.status === 'overdue' || dl.status === 'critical'; });
+ if (hot.length) {
+ _showHotDeadlines(hot);
+ } else {
+ toast('📅 Сроки пересчитаны на ' + d);
+ }
+ if (data.council_trigger) setTimeout(function(){ _offerCouncil(data); }, 1000);
+ }
+ })
+ .catch(function(){ toast('📅 Дата сохранена'); });
+}
+
+function _showHotDeadlines(hot) {
+ var wrap = document.querySelector('.chatwrap');
+ if (!wrap) return;
+ var list = hot.map(function(d){
+ return '' +
+ '
' + d.title + ' —
' + (d.status_label || d.date) + '' +
+ (d.quote ? '
' + d.quote + '
' : '') +
+ '
';
+ }).join('');
+ var div = document.createElement('div');
+ div.innerHTML =
+ '' +
+ '
Елена
' +
+ '🔴 Обратите внимание — есть горящие сроки:' +
+ '
' + list + '
' +
+ '
';
+ wrap.appendChild(div);
+ div.scrollIntoView({behavior:'smooth'});
+}
+
+function _offerCouncil(data) {
+ /* Предлагаем Council для сложных кейсов */
+ var wrap = document.querySelector('.chatwrap');
+ if (!wrap) return;
+ var old = document.getElementById('council-offer'); if(old) return; // уже показано
+ var div = document.createElement('div');
+ div.id = 'council-offer';
+ div.innerHTML =
+ '' +
+ '
Елена
' +
+ 'Ситуация требует глубокого анализа — я вижу признаки сложного кейса.' +
+ '
' +
+ '' +
+ '
⚖️ Совет экспертов
' +
+ '
' +
+ 'Адвокат · Судья · Арбитражный управляющий · Пристав · Аналитик практики — ' +
+ 'каждый даёт позицию по вашему делу. Opus синтезирует вердикт с шансами в суде.' +
+ '
' +
+ '
от 2 990 ₽ · ~20 сек
' +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '
';
+ wrap.appendChild(div);
+ div.scrollIntoView({behavior:'smooth'});
+}
+
+function _runCouncil() {
+ var offer = document.getElementById('council-offer'); if(offer) offer.remove();
+ var text = (document.getElementById('el-paste').value || '').trim();
+ if (!text) { toast('Нет текста договора'); return; }
+
+ var wrap = document.querySelector('.chatwrap');
+ var progress = document.createElement('div');
+ progress.id = 'council-progress';
+ progress.innerHTML =
+ '' +
+ '
Елена
' +
+ '⚖️ Совет экспертов работает…
0 сек' +
+ '
';
+ if (wrap) wrap.appendChild(progress);
+ progress.scrollIntoView({behavior:'smooth'});
+
+ var t0 = Date.now();
+ var timerInterval = setInterval(function(){
+ var el = document.getElementById('council-timer');
+ if (el) el.textContent = Math.round((Date.now()-t0)/1000) + ' сек';
+ }, 1000);
+
+ fetch(API_BASE + '/api/council', {
+ method: 'POST',
+ headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({
+ case_description: text,
+ deadlines: _contractDeadlines,
+ complexity_score: 3
+ })
+ })
+ .then(function(r){ return r.json(); })
+ .then(function(data){
+ clearInterval(timerInterval);
+ var pr = document.getElementById('council-progress'); if(pr) pr.remove();
+ _showCouncilResult(data);
+ })
+ .catch(function(e){
+ clearInterval(timerInterval);
+ var pr = document.getElementById('council-progress'); if(pr) pr.remove();
+ toast('Ошибка совета: ' + e.message);
+ });
+}
+
+function _showCouncilResult(data) {
+ var wrap = document.querySelector('.chatwrap');
+ if (!wrap) return;
+
+ // Карточки агентов
+ var agentCards = '';
+ var icons = {advocate:'⚖️', judge:'🏛️', arbitrator:'👨💼', bailiff:'🔨', precedents:'📚'};
+ if (data.agents) {
+ Object.keys(data.agents).forEach(function(key){
+ var a = data.agents[key];
+ agentCards +=
+ '' +
+ '
' + (icons[key]||'•') + ' ' + a.label + '
' +
+ '
' + a.reply.replace(/\n/g,'
') + '
' +
+ '
';
+ });
+ }
+
+ var div = document.createElement('div');
+ div.innerHTML =
+ '' +
+ '
Елена · Вердикт совета (' + (data.duration_sec||'?') + ' сек)
' +
+ '
' +
+ (data.synthesis || '').replace(/\n/g,'
') +
+ '
' +
+ '
Позиции экспертов ↓
' +
+ '' + agentCards + '
' +
+ '' +
+ '
';
+ wrap.appendChild(div);
+ div.scrollIntoView({behavior:'smooth'});
+}
+
/* ── ВЫБОР DELIVERABLE ── */
const DELIVS = {
protocol: {
@@ -3861,6 +4094,48 @@ function heroChatReply(key) {
}, 400);
}
+/* ── API ── */
+var API_BASE = 'http://localhost:5001'; // замени на VPS когда задеплоишь
+var _apiAvailable = null; // null=не проверяли, true/false
+(function _checkApi(){
+ fetch(API_BASE + '/api/health', {method:'GET'})
+ .then(function(r){ return r.json(); })
+ .then(function(d){ _apiAvailable = d.has_api_key && d.status === 'ok'; })
+ .catch(function(){ _apiAvailable = false; });
+})();
+
+// Текущий контекст договора (deadlines из последнего анализа)
+var _contractDeadlines = null;
+var _chatHistory = []; // история чата для /api/elena
+
+function _elenaApi(txt, intent, callback) {
+ /* Вызывает /api/elena. При ошибке — fallback на шаблон. */
+ if (!_apiAvailable) { callback(null); return; }
+ var empathy = _getEmpathyPrefix(txt);
+ var reply = _HC_REPLIES[intent] || _HC_REPLIES.question;
+ fetch(API_BASE + '/api/elena', {
+ method: 'POST',
+ headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({
+ text: txt,
+ intent: intent,
+ history: _chatHistory.slice(-6),
+ deadlines: _contractDeadlines
+ })
+ })
+ .then(function(r){ return r.json(); })
+ .then(function(d){
+ if (d.reply) {
+ _chatHistory.push({role:'user', content: txt});
+ _chatHistory.push({role:'assistant', content: d.reply});
+ callback(d.reply);
+ } else {
+ callback(null);
+ }
+ })
+ .catch(function(){ callback(null); });
+}
+
/* ── КЛАССИФИКАТОР ВХОДЯЩИХ СООБЩЕНИЙ ── */
var _offtopicCount = 0;
@@ -3914,16 +4189,17 @@ function heroChatSend() {
else if (/состав|написат|создат|подготов|нужен договор|оформить|нужна расписка/.test(t) && !/проверит/.test(t)) intent = 'create';
else if (/проверит|анализ|посмотр|риск|подписать|боюсь подписать|прислали договор|дали договор/.test(t)) intent = 'check';
var reply = _HC_REPLIES[intent] || _HC_REPLIES.question;
- // Ответ Елены = эмпатия к боли + конкретное действие
+ // Ответ Елены — реальный API или fallback на шаблон
var empathy = _getEmpathyPrefix(txt);
- var elenaText = empathy + reply.elena;
+ var fallbackText = empathy + reply.elena;
setTimeout(function(){
_hcAddTyping();
- setTimeout(function(){
+ _elenaApi(txt, intent, function(apiReply){
_hcRemoveTyping();
+ var elenaText = apiReply || fallbackText;
_hcAddBubble(elenaText, false);
setTimeout(function(){ _chatTransition(txt, intent); }, 1400);
- }, 900);
+ });
}, 400);
return;
}