mirror of
https://github.com/wasrusgen/zashita-brandbook.git
synced 2026-06-03 17:44:47 +00:00
feat: informed consent log - Elena warns before risky additions, all edits logged in case map
This commit is contained in:
parent
90c77f2ae4
commit
b7066a1ba4
148
mockup.html
148
mockup.html
@ -4896,46 +4896,146 @@ function _setDocMode(mode) {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Режим 1: Елена добавляет через API
|
// ── ЖУРНАЛ ИНФОРМИРОВАНИЯ ────────────────────────────────────────────────────
|
||||||
|
// Фиксирует факт уведомления клиента о рисках/нарушениях
|
||||||
|
// Хранится в localStorage и показывается в Карте дела
|
||||||
|
|
||||||
|
function _logInformed(subject, elenaText, clientResponse) {
|
||||||
|
var ts = new Date().toISOString();
|
||||||
|
var docTitle = (_docData && _docData.title) || 'документ';
|
||||||
|
var entry = {
|
||||||
|
ts: ts,
|
||||||
|
subject: subject,
|
||||||
|
warning: elenaText,
|
||||||
|
response: clientResponse || 'принял к сведению',
|
||||||
|
doc: docTitle
|
||||||
|
};
|
||||||
|
// В карту дела
|
||||||
|
_addCaseNote('informed',
|
||||||
|
'Уведомлён: ' + subject + ' · Ответ: ' + entry.response,
|
||||||
|
entry
|
||||||
|
);
|
||||||
|
// В историю переписки
|
||||||
|
_chatHistory.push({
|
||||||
|
role: 'assistant',
|
||||||
|
content: '[Информирование зафиксировано] ' + ts.slice(0,10) +
|
||||||
|
' · ' + subject + ' · Ответ клиента: ' + entry.response
|
||||||
|
});
|
||||||
|
_saveHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Режим 1: Елена добавляет через API + проверяет на нарушения
|
||||||
function _elenaAddToDoc() {
|
function _elenaAddToDoc() {
|
||||||
var inp = document.getElementById('elena-add-inp');
|
var inp = document.getElementById('elena-add-inp');
|
||||||
if (!inp || !inp.value.trim()) return;
|
if (!inp || !inp.value.trim()) return;
|
||||||
var request = inp.value.trim();
|
var request = inp.value.trim();
|
||||||
inp.value = '';
|
inp.value = '';
|
||||||
inp.placeholder = 'Елена думает...';
|
inp.placeholder = 'Елена проверяет...';
|
||||||
inp.disabled = true;
|
inp.disabled = true;
|
||||||
|
|
||||||
var currentText = '';
|
var currentText = '';
|
||||||
var el = document.getElementById('tpl-doc-text');
|
var el = document.getElementById('tpl-doc-text');
|
||||||
if (el) currentText = el.tagName === 'TEXTAREA' ? el.value : (el.textContent||el.innerText||'');
|
if (el) currentText = el.tagName === 'TEXTAREA' ? el.value : (el.textContent||el.innerText||'');
|
||||||
|
|
||||||
|
// Сначала проверяем через Елену — нет ли нарушений в запросе
|
||||||
|
_elenaApi(
|
||||||
|
'Клиент просит добавить в документ: «' + request + '». ' +
|
||||||
|
'Оцени: (1) это законно по ГК/ТК РФ? (2) не противоречит ли тексту документа? ' +
|
||||||
|
'Если есть риск — предупреди ОДНИМ предложением. Если всё ок — ответь «Добавляю».',
|
||||||
|
'question',
|
||||||
|
function(checkReply) {
|
||||||
|
var hasRisk = checkReply && !/добавляю|всё\s*ок|без\s*замечаний/i.test(checkReply);
|
||||||
|
|
||||||
|
if (hasRisk) {
|
||||||
|
// Показываем предупреждение с выбором
|
||||||
|
inp.disabled = false;
|
||||||
|
inp.placeholder = 'Опишите что добавить...';
|
||||||
|
|
||||||
|
var warnDiv = document.getElementById('elena-add-warning');
|
||||||
|
if (warnDiv) warnDiv.remove();
|
||||||
|
var w = document.createElement('div');
|
||||||
|
w.id = 'elena-add-warning';
|
||||||
|
w.style.cssText = 'background:#fffbeb;border:1.5px solid #fcd34d;border-radius:10px;padding:12px;margin-top:8px;font-size:13px';
|
||||||
|
w.innerHTML =
|
||||||
|
'<div style="font-weight:600;margin-bottom:6px">Елена предупреждает:</div>' +
|
||||||
|
'<div style="color:#92400e;margin-bottom:10px">' + checkReply + '</div>' +
|
||||||
|
'<div style="display:flex;gap:8px">' +
|
||||||
|
'<button class="btn btn-o" style="padding:6px 12px;font-size:12px" ' +
|
||||||
|
'onclick="_proceedWithAddition(\'' + request.replace(/'/g,"\\'") + '\',\'' + (checkReply||'').replace(/'/g,"\\'").slice(0,100) + '\')">' +
|
||||||
|
'Добавить всё равно</button>' +
|
||||||
|
'<button class="svc-btn-detail" style="font-size:12px" onclick="this.closest(\'#elena-add-warning\').remove()">Отмена</button>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
var panel = document.getElementById('doc-mode-panel');
|
||||||
|
if (panel) panel.appendChild(w);
|
||||||
|
|
||||||
|
// Логируем факт предупреждения
|
||||||
|
_logInformed(
|
||||||
|
'Риск при добавлении пункта: «' + request.slice(0,60) + '»',
|
||||||
|
checkReply,
|
||||||
|
'ожидает решения клиента'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Нет рисков — добавляем
|
||||||
|
_proceedWithAddition(request, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _proceedWithAddition(request, warningText) {
|
||||||
|
// Убираем предупреждение если было
|
||||||
|
var w = document.getElementById('elena-add-warning');
|
||||||
|
if (w) w.remove();
|
||||||
|
|
||||||
|
var inp = document.getElementById('elena-add-inp');
|
||||||
|
if (inp) { inp.disabled = true; inp.placeholder = 'Добавляю...'; }
|
||||||
|
|
||||||
|
var currentText = '';
|
||||||
|
var el = document.getElementById('tpl-doc-text');
|
||||||
|
if (el) currentText = el.tagName === 'TEXTAREA' ? el.value : (el.textContent||el.innerText||'');
|
||||||
|
|
||||||
|
// Если клиент решил добавить несмотря на предупреждение — логируем
|
||||||
|
if (warningText) {
|
||||||
|
_logInformed(
|
||||||
|
'Добавлен пункт несмотря на предупреждение: «' + request.slice(0,60) + '»',
|
||||||
|
warningText,
|
||||||
|
'клиент добавил несмотря на риск'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fetch(API_BASE + '/api/generate', {
|
fetch(API_BASE + '/api/generate', {
|
||||||
method: 'POST', headers: {'Content-Type':'application/json'},
|
method: 'POST', headers: {'Content-Type':'application/json'},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
template: (_tplCurrent && _tplCurrent.key) || 'custom',
|
template: (_tplCurrent && _tplCurrent.key) || 'custom',
|
||||||
parties: (_tplCurrent && _tplCurrent.parties) || {},
|
parties: (_tplCurrent && _tplCurrent.parties) || {},
|
||||||
contract_data: (_tplCurrent && _tplCurrent.contract_data) || {},
|
contract_data: (_tplCurrent && _tplCurrent.contract_data) || {},
|
||||||
extra: 'ТЕКУЩИЙ ДОКУМЕНТ:\n' + currentText.slice(0, 2000) + '\n\nЗАПРОС КЛИЕНТА: ' + request +
|
extra: 'ТЕКУЩИЙ ДОКУМЕНТ:\n' + currentText.slice(0, 2000) +
|
||||||
'\n\nДобавь нужный фрагмент в конец документа в том же стиле. Верни ТОЛЬКО дополнение (не весь документ).'
|
'\n\nЗАПРОС: ' + request +
|
||||||
|
'\n\nДобавь фрагмент в конец в том же стиле. Верни ТОЛЬКО добавление.'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(function(r){ return r.json(); })
|
.then(function(r){ return r.json(); })
|
||||||
.then(function(d) {
|
.then(function(d) {
|
||||||
inp.disabled = false;
|
if (inp) { inp.disabled = false; inp.placeholder = 'Опишите что добавить...'; }
|
||||||
inp.placeholder = 'Опишите что добавить...';
|
|
||||||
if (d.error) { toast('Ошибка: ' + d.error); return; }
|
if (d.error) { toast('Ошибка: ' + d.error); return; }
|
||||||
var addition = '\n\n' + (d.text || '');
|
var addition = '\n\n' + (d.text || '');
|
||||||
// Добавляем к тексту
|
|
||||||
var textEl = document.getElementById('tpl-doc-text');
|
var textEl = document.getElementById('tpl-doc-text');
|
||||||
if (textEl) {
|
if (textEl) {
|
||||||
if (textEl.tagName === 'TEXTAREA') textEl.value += addition;
|
if (textEl.tagName === 'TEXTAREA') textEl.value += addition;
|
||||||
else textEl.textContent += addition;
|
else textEl.textContent += addition;
|
||||||
}
|
}
|
||||||
if (_docData) _docData.text = (_docData.text || '') + addition;
|
if (_docData) _docData.text = (_docData.text || '') + addition;
|
||||||
toast('✅ Елена добавила пункт');
|
toast('✅ Пункт добавлен');
|
||||||
_addCaseNote('decision', 'Документ дополнен: ' + request, {});
|
// Логируем добавление
|
||||||
|
if (!warningText) {
|
||||||
|
_addCaseNote('decision', 'Документ дополнен по запросу: ' + request.slice(0,80), {});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(function() { inp.disabled = false; toast('Ошибка'); });
|
.catch(function() {
|
||||||
|
if (inp) { inp.disabled = false; inp.placeholder = 'Опишите что добавить...'; }
|
||||||
|
toast('Ошибка генерации');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Режим 2: Готовый блок
|
// Режим 2: Готовый блок
|
||||||
@ -4951,17 +5051,25 @@ function _addBlockToDoc(blockText) {
|
|||||||
_setDocMode('view'); // возвращаемся в просмотр
|
_setDocMode('view'); // возвращаемся в просмотр
|
||||||
}
|
}
|
||||||
|
|
||||||
// Режим 3: Сохранить прямое редактирование
|
// Режим 3: Сохранить прямое редактирование + логируем информирование
|
||||||
function _saveDocEdits() {
|
function _saveDocEdits() {
|
||||||
var textEl = document.getElementById('tpl-doc-text');
|
var textEl = document.getElementById('tpl-doc-text');
|
||||||
if (textEl && _docData) {
|
if (textEl && _docData) {
|
||||||
_docData.text = textEl.tagName === 'TEXTAREA' ? textEl.value : (textEl.textContent||'');
|
_docData.text = textEl.tagName === 'TEXTAREA' ? textEl.value : (textEl.textContent||'');
|
||||||
_addCaseNote('decision', 'Документ отредактирован клиентом самостоятельно', { ts: new Date().toISOString() });
|
// Логируем как факт информирования
|
||||||
|
_logInformed(
|
||||||
|
'Документ «' + (_docData.title||'без названия') + '» отредактирован клиентом самостоятельно',
|
||||||
|
'ЗАЩИТА — информационный сервис. Ответственность за внесённые изменения несёт клиент.',
|
||||||
|
'клиент подтвердил сохранение изменений'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_setDocMode('view');
|
_setDocMode('view');
|
||||||
toast('✅ Изменения сохранены');
|
toast('✅ Изменения сохранены и зафиксированы в карте дела');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Режим 2: Логируем добавление готового блока
|
||||||
|
var _origAddBlock = typeof _addBlockToDoc === 'function' ? _addBlockToDoc : null;
|
||||||
|
|
||||||
function _printDoc() {
|
function _printDoc() {
|
||||||
var text = document.getElementById('tpl-doc-text');
|
var text = document.getElementById('tpl-doc-text');
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
@ -8342,6 +8450,20 @@ function renderCaseMap() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Секция: Факты информирования (юридически значимые)
|
||||||
|
var informed = notes.filter(function(n){ return n.type === 'informed'; });
|
||||||
|
if (informed.length) {
|
||||||
|
html += _cmSection('📋 Факты информирования', '#7c3aed', informed.map(function(n){
|
||||||
|
var d = n.meta || {};
|
||||||
|
return {
|
||||||
|
text: n.text,
|
||||||
|
icon: '📋',
|
||||||
|
sub: d.warning ? 'Предупреждение: ' + d.warning.slice(0,80) : '',
|
||||||
|
date: n.ts
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// Секция: Зафиксированные риски
|
// Секция: Зафиксированные риски
|
||||||
var risks = notes.filter(function(n){ return n.type === 'risk' || n.type === 'acknowledged'; });
|
var risks = notes.filter(function(n){ return n.type === 'risk' || n.type === 'acknowledged'; });
|
||||||
if (risks.length) {
|
if (risks.length) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user