lending-teoriya-pod-sebya/script.js

335 lines
13 KiB
JavaScript
Raw Permalink 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.

// Теория под себя — интерактив: сценарии, чат Töre, форма ожидания
(function () {
"use strict";
var STORE_KEY = "tore-theory-chat-v1";
var WAIT_KEY = "tore-theory-wait-v1";
var MAX_STORED = 30;
var SCENARIOS = {
habits: {
title: "Привычки",
question: "Покажи, какие мои утренние привычки повторяются каждый день.",
answer:
"Собрал за 14 дней подряд: подъём до 08:00 — 12 раз, прогулка 20 минут — 10, кофе до работы — 14 из 14.",
points: [
"Самая устойчивая связка: подъём + прогулка до 09:00.",
"В дни без прогулки уровень энергии к обеду заметно ниже.",
"Предлагаю закрепить прогулку в 08:15 на этой неделе."
],
followups: ["Что добавить в утро?", "Как выглядит типичный день?"]
},
time: {
title: "Распределение времени",
question: "Куда уходит больше всего времени по будням?",
answer:
"Разложил 24 рабочих часа по блокам. Топ: фокус-задачи — 9 ч, переписки и звонки — 6 ч, обучение — 3 ч.",
points: [
"Больше всего «съедает» переписка по средам и пятницам.",
"Фокус-задачи лучше всего получаются до 11:00.",
"Если собрать переписку в один слот в 13:00, высвобождается около 1,5 ч."
],
followups: [
"Где я теряю время?",
"Что сдвинуть, чтобы найти час на обучение?"
]
},
results: {
title: "Результаты за неделю",
question: "Сравни мою неделю с прошлой: что выросло?",
answer:
"Сверил две недели по трём метрикам: закрытых задач 18 против 14, новых навыков в работе 2 против 0, просроченных — 1 против 3.",
points: [
"Рост стабильный третий период подряд: в среднем +12% по закрытым задачам.",
"Просрочки упали после того, как приоритеты стали меняться только утром.",
"На следующей неделе стоит добавить ещё один глубокий фокус-слот."
],
followups: ["Составь план на след. неделю", "Что мешает росту?"]
}
};
var GENERAL_ANSWERS = [
{
keys: ["привычк", "утро", "подъём"],
text: {
answer: "Посмотрел по дням: устойчивых повторов больше, чем кажется. Твоя «сильная» зона — начало дня.",
points: [
"Регулярнее всего повторяются подъём, кофе и короткая разминка.",
"Новые вещи закрепляются, если ставить их сразу после устойчивого шага."
]
}
},
{
keys: ["время", "час", "растор", "потеря"],
text: {
answer: "Посчитаю по блокам времени: где оно собирается, а где разбивается на мелкие куски.",
points: [
"Длинные фокус-блоки редки: чаще 25–35 минут до прерывания.",
"Лучший приём недели — собрать мелкие дела в один вечерний слот."
]
}
},
{
keys: ["результат", "недел", "рост", "показател", "сравн"],
text: {
answer: "По последним периодам динамика вверх: ключевые цифры растут второй период кряду.",
points: [
"Рост ровный, без провалов — значит, методика сработала.",
"Следующая точка роста — добавить один новый фокус-слот."
]
}
},
{
keys: ["план", "рекомендац", "что делать", "совет"],
text: {
answer: "Вот короткая рекомендация на ближайшие 7 дней, под твой текущий ритм.",
points: [
"Фиксировать утро: два шага, без выбора и откладывания.",
"Один большой блок работы до обеда, без переписок.",
"В воскресенье — 15 минут на сверку: что сработало, что нет."
],
followups: ["Составь план на неделю", "Что мешает росту?"]
}
},
{
keys: ["план", "след"],
text: {
answer: "Собрал план на неделю по твоим метрикам.",
points: [
"Пн–чт: по одному фокус-блоку на главную задачу.",
"Пт: разбор недели, 20 минут, без новых задач."
],
}
}
];
function byId(id) { return document.getElementById(id); }
function esc(s) {
return String(s).replace(/[&<>"]/g, function (c) {
return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c];
});
}
function loadChat() {
try {
var raw = localStorage.getItem(STORE_KEY);
if (!raw) return [];
var arr = JSON.parse(raw);
return Array.isArray(arr) ? arr : [];
} catch (e) { return []; }
}
function saveChat(list) {
try {
if (list.length > MAX_STORED) list = list.slice(list.length - MAX_STORED);
localStorage.setItem(STORE_KEY, JSON.stringify(list));
} catch (e) { /* квота — молча пропускаем */ }
}
function msgHtml(m) {
if (m.role === "user") {
return '<div class="tk-msg tk-msg-user"><div class="tk-msg-head">Ты</div>' + esc(m.text) + "</div>";
}
var html = '<div class="tk-msg tk-msg-ai"><div class="tk-msg-head">' +
'<svg class="kt-icon kt-icon-sm" aria-hidden="true"><use href="#sparkles"></use></svg>' +
"Töre · " + esc(m.time) + "</div>";
if (m.text) html += "<p>" + esc(m.text) + "</p>";
if (m.points && m.points.length) {
html += '<ul class="tk-msg-points">';
for (var i = 0; i < m.points.length; i++) html += "<li>" + esc(m.points[i]) + "</li>";
html += "</ul>";
}
if (m.followups && m.followups.length) {
html += '<div class="tk-msg-actions">';
for (var j = 0; j < m.followups.length; j++) {
html += '<button class="tk-prompt-chip" data-q="' + esc(m.followups[j]) + '">' + esc(m.followups[j]) + "</button>";
}
html += "</div>";
}
return html + "</div>";
}
function appendMsg(m, stored) {
var log = byId("chat-log");
if (!log) return;
var div = document.createElement("div");
div.innerHTML = msgHtml(m);
var node = div.firstChild;
log.appendChild(node);
log.scrollTop = log.scrollHeight;
if (stored) {
var list = loadChat();
list.push(m);
saveChat(list);
}
}
function renderStored(list) {
var log = byId("chat-log");
if (!log) return;
log.innerHTML = "";
for (var i = 0; i < list.length; i++) {
var div = document.createElement("div");
div.innerHTML = msgHtml(list[i]);
log.appendChild(div.firstChild);
}
log.scrollTop = log.scrollHeight;
}
function answerFor(text) {
var t = text.toLowerCase();
for (var i = 0; i < GENERAL_ANSWERS.length; i++) {
var keys = GENERAL_ANSWERS[i].keys;
for (var j = 0; j < keys.length; j++) {
if (t.indexOf(keys[j]) !== -1) return GENERAL_ANSWERS[i].text;
}
}
return FALLBACK;
}
function nowTime() {
var d = new Date();
var hh = ("0" + d.getHours()).slice(-2);
var mm = ("0" + d.getMinutes()).slice(-2);
return hh + ":" + mm;
}
function sendUser(text) {
if (!text) return;
appendMsg({ role: "user", text: text, time: nowTime() }, true);
setTimeout(function () {
var res = answerFor(text);
var msg = { role: "ai", text: res.answer, points: res.points || null, time: nowTime() };
if (res.points) msg.followups = res.followups || null;
appendMsg(msg, true);
}, 450);
}
function initChat() {
var log = byId("chat-log");
if (!log) return;
var stored = loadChat();
if (stored.length) {
renderStored(stored);
} else {
appendMsg({
role: "ai",
text: "Привет! Я Töre — ИИ-аналитик. Выбери сценарий выше или спроси меня о своих данных: привычки, время, результаты.",
points: null,
time: nowTime()
}, false);
}
var form = byId("chat-form");
var input = byId("chat-input");
if (form && input) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var v = input.value.trim();
if (!v) return;
input.value = "";
input.style.height = "auto";
sendUser(v);
});
input.addEventListener("keydown", function (e) {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
form.requestSubmit();
}
});
input.addEventListener("input", function () {
input.style.height = "auto";
input.style.height = Math.min(input.scrollHeight, 120) + "px";
});
}
// Чипы-продолжения: делегирование
log.addEventListener("click", function (e) {
var btn = e.target;
while (btn && btn.tagName !== "BUTTON") btn = btn.parentNode;
if (!btn || !btn.classList || !btn.classList.contains("tk-prompt-chip")) return;
var q = btn.getAttribute("data-q");
if (q) sendUser(q);
});
}
function initScenarios() {
var tabs = document.querySelectorAll("#scenario-list .kt-ai-tab");
for (var i = 0; i < tabs.length; i++) {
(function (tab) {
tab.addEventListener("click", function () {
var id = tab.getAttribute("data-scenario");
for (var k = 0; k < tabs.length; k++) tabs[k].setAttribute("data-active", "false");
tab.setAttribute("data-active", "true");
var s = SCENARIOS[id];
if (!s) return;
sendUser(s.question);
});
})(tabs[i]);
}
}
function initWait() {
var input = byId("wait-email");
var btn = byId("wait-submit");
var note = byId("wait-note");
var field = input ? input.closest(".kt-ai-field") : null;
if (!input || !btn) return;
var saved = "";
try { saved = localStorage.getItem(WAIT_KEY) || ""; } catch (e) {}
if (saved) {
input.value = saved;
if (note) note.textContent = "Сохранено: " + saved + ". При запуске пришлём уведомление.";
}
btn.addEventListener("click", function () {
var v = input.value.trim();
if (field) field.setAttribute("data-invalid", "false");
if (!v) {
if (field) field.setAttribute("data-invalid", "true");
input.focus();
if (note) note.textContent = "Введите адрес, чтобы сохранить заявку.";
return;
}
if (v.indexOf("@") === -1 || v.indexOf("@") === 0 || v.indexOf("@") === v.length - 1) {
if (field) field.setAttribute("data-invalid", "true");
if (note) note.textContent = "Похоже, в адресе опечатка — проверь формат.";
return;
}
try { localStorage.setItem(WAIT_KEY, v); } catch (e) {}
if (note) note.textContent = "Сохранено: " + v + ". При запуске пришлём уведомление.";
});
input.addEventListener("input", function () {
if (field) field.setAttribute("data-invalid", "false");
});
}
function initNav() {
var links = document.querySelectorAll("a[href^='#']");
for (var i = 0; i < links.length; i++) {
(function (a) {
a.addEventListener("click", function () {
var id = a.getAttribute("href");
if (id.length > 1) {
var el = byId(id.slice(1));
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
}
});
})(links[i]);
}
}
function init() {
initChat();
initScenarios();
initWait();
initNav();
var hint = byId("chat-hint");
if (hint) hint.textContent = "Диалог сохраняется в этом браузере — можно вернуться позже.";
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();