Analyzing diff...
plans/alyona/weeks-9-10/index.html+149 −56
index 33c98d7..948f391 100644 @@ -164,16 +164,6 @@ box-shadow: var(--shadow); margin: 0 0 34px; } - .results-action { display: flex; justify-content: center; margin: -10px 0 26px; } - .results-button, .results-secondary { border: 0; border-radius: 999px; padding: 14px 22px; font: inherit; font-weight: 850; cursor: pointer; } - .results-button { background: var(--navy); color: #fff; box-shadow: var(--shadow); } - .results-secondary { background: #dfeaf5; color: var(--navy); } - .results-dialog { width: min(680px, calc(100% - 28px)); border: 0; border-radius: 24px; padding: 22px; box-shadow: 0 24px 80px rgba(20,48,75,.28); } - .results-dialog::backdrop { background: rgba(20,48,75,.55); } - .results-dialog h2 { margin: 0 0 8px; color: var(--navy); } - .results-dialog p { color: var(--muted); } - .results-preview { width: 100%; min-height: 270px; box-sizing: border-box; resize: vertical; border: 1px solid #c9d8e7; border-radius: 14px; padding: 12px; font: inherit; } - .results-dialog-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; } .progress-top { display: flex; justify-content: space-between; @@ -210,6 +200,79 @@ color: var(--muted); font-size: 0.85rem; } + .report-actions { + display: flex; + justify-content: flex-end; + margin-top: 14px; + } + .report-button, + .modal-button { + border: 0; + border-radius: 14px; + padding: 11px 16px; + background: var(--navy); + color: var(--white); + font-weight: 800; + cursor: pointer; + } + .report-button:hover, + .modal-button:hover { + background: var(--blue); + } + .modal-button.secondary { + background: var(--sky); + color: var(--navy); + border: 1px solid var(--line); + } + .report-dialog { + width: min(720px, calc(100% - 28px)); + max-height: calc(100dvh - 28px); + padding: 0; + border: 0; + border-radius: 24px; + color: var(--ink); + box-shadow: 0 24px 80px rgba(16, 43, 85, 0.28); + } + .report-dialog::backdrop { + background: rgba(16, 43, 85, 0.58); + backdrop-filter: blur(4px); + } + .report-dialog-inner { + padding: 24px; + } + .report-dialog h2 { + margin: 0; + color: var(--navy); + } + .report-dialog p { + margin: 8px 0 16px; + color: var(--muted); + } + .report-preview { + display: block; + width: 100%; + min-height: 300px; + max-height: 48dvh; + resize: vertical; + padding: 14px; + border: 1px solid var(--line); + border-radius: 14px; + background: var(--paper); + color: var(--ink); + } + .report-dialog-actions { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 10px; + margin-top: 16px; + } + .copy-status { + min-height: 1.4em; + margin-top: 8px; + color: var(--ok); + font-weight: 700; + } .weeks-grid { display: grid; grid-template-columns: minmax(0, 1fr); @@ -636,7 +699,6 @@ </p> </header> <main class="shell"> - <div class="results-action"><button class="results-button" id="openResults" type="button">Отправить итоги куратору</button></div> <section class="progress-panel" aria-label="Прогресс плана"> <div class="progress-top"> <span class="progress-title">Прогресс тренировок</span @@ -648,18 +710,12 @@ ><span id="week9Stat">Неделя 10: 0 из 7</span ><span id="practiceStat">Практики: 0 отметок</span> </div> - </section> - <dialog class="results-dialog" id="resultsDialog"> - <h2>Итоги для @manoraya</h2> - <p>Проверьте текст. Ничего не отправляется автоматически: сначала скопируйте итоги, затем откройте чат и вставьте их.</p> - <textarea class="results-preview" id="resultsPreview" aria-label="Предпросмотр итогов"></textarea> - <div class="results-dialog-actions"> - <button class="results-button" id="copyResults" type="button">Скопировать итоги</button> - <button class="results-button" id="openCurator" type="button">Открыть @manoraya в Telegram</button> - <button class="results-secondary" id="closeResults" type="button">Закрыть</button> + <div class="report-actions"> + <button class="report-button" id="openReport" type="button"> + Отправить итоги куратору + </button> </div> - <p id="resultsStatus" role="status" aria-live="polite"></p> - </dialog> + </section> <div class="weeks-grid"> <section class="week" data-week="8"> @@ -748,6 +804,23 @@ Все отметки и комментарии сохраняются только в браузере на этом устройстве. </footer> + <dialog class="report-dialog" id="reportDialog"> + <div class="report-dialog-inner"> + <h2>Итоги для куратора</h2> + <p> + Проверьте текст. Кнопка «Открыть чат» скопирует его и откроет + Telegram-диалог с @manoraya — останется вставить текст и подтвердить + отправку. + </p> + <textarea class="report-preview" id="reportPreview" aria-label="Текст итогов"></textarea> + <div class="copy-status" id="copyStatus" aria-live="polite"></div> + <div class="report-dialog-actions"> + <button class="modal-button secondary" id="closeReport" type="button">Закрыть</button> + <button class="modal-button secondary" id="copyReport" type="button">Скопировать</button> + <button class="modal-button" id="sendReport" type="button">Открыть чат @manoraya</button> + </div> + </div> + </dialog> <script> const W8 = [ [ @@ -966,40 +1039,7 @@ localStorage.setItem(KEY, JSON.stringify(state)); updateProgress(); }; - function buildResults() { - const completed = [...document.querySelectorAll(".check:checked")].map((check) => { - const workout = check.closest(".workout"); - const day = check.closest(".day").querySelector("h3").textContent; - const week = check.dataset.id.startsWith("w8-") ? "Неделя 9" : "Неделя 10"; - return `${week}, ${day}: ${workout.querySelector("a").textContent}`; - }); - const comments = Object.entries(state.comments || {}).filter(([, value]) => String(value).trim()).map(([id, value]) => { - const field = document.querySelector(`[data-comment="${id}"]`); - const title = field ? field.closest(".workout").querySelector("a").textContent : id; - return `${title}: ${String(value).trim()}`; - }); - const practices = []; - [8, 9].forEach((week) => TECH.forEach((title, technique) => { - const days = DAYS.filter((_, day) => state.practice[`w${week}-p${technique}-d${day}`]); - if (days.length) practices.push(`Неделя ${week === 8 ? 9 : 10}: ${title} — ${days.join(", ")}`); - })); - const total = document.querySelectorAll(".check").length; - return ["Итоги по плану Алёны · недели 9–10", `Тренировки: ${completed.length} из ${total}`, "", "Выполнено:", completed.length ? completed.map((x) => `• ${x}`).join("\n") : "— пока без отметок", "", "Техники:", practices.length ? practices.map((x) => `• ${x}`).join("\n") : "— пока без отметок", "", "Комментарии:", comments.length ? comments.map((x) => `• ${x}`).join("\n") : "— нет комментариев", "", location.href].join("\n"); - } - const resultsDialog = document.getElementById("resultsDialog"); - document.getElementById("openResults").onclick = () => { - document.getElementById("resultsPreview").value = buildResults(); - document.getElementById("resultsStatus").textContent = ""; - resultsDialog.showModal(); - }; - document.getElementById("copyResults").onclick = async () => { - const preview = document.getElementById("resultsPreview"); - try { await navigator.clipboard.writeText(preview.value); } - catch (_) { preview.select(); document.execCommand("copy"); } - document.getElementById("resultsStatus").textContent = "Итоги скопированы. Теперь откройте чат и вставьте сообщение."; - }; - document.getElementById("openCurator").onclick = () => window.open("https://t.me/manoraya", "_blank", "noopener"); - document.getElementById("closeResults").onclick = () => resultsDialog.close(); + const workoutLabels = {}; function renderWeek(target, data, week) { const root = document.getElementById(target); data.forEach((d, di) => { @@ -1008,6 +1048,7 @@ day.innerHTML = `<div class="day-head"><h3>${d[0]}</h3><span class="day-type">${d[1]}</span></div>`; d[2].forEach((w, wi) => { const id = `w${week}-d${di}-t${wi}`; + workoutLabels[id] = `Неделя ${week === 8 ? 9 : 10}, ${d[0]} — ${w[0]}`; const row = document.createElement("div"); row.className = "workout"; row.innerHTML = `<input class="check" type="checkbox" aria-label="Отметить тренировку выполненной" data-id="${id}" ${state.done[id] ? "checked" : ""}><div><a href="${w[1]}" target="_blank" rel="noopener">${w[0]}</a><div class="meta">${w[3]}</div></div><span class="duration">${w[2]}</span><textarea class="comment" data-comment="${id}" placeholder="Комментарий после тренировки">${state.comments[id] || ""}</textarea>`; @@ -1176,6 +1217,46 @@ document.getElementById("practiceStat").textContent = `Практики: ${Object.values(state.practice).filter(Boolean).length} отметок`; } + function buildReport() { + const checks = [...document.querySelectorAll(".check")]; + const done = checks.filter((c) => c.checked); + const comments = Object.entries(state.comments) + .filter(([, value]) => value && value.trim()) + .map(([id, value]) => `• ${workoutLabels[id] || id}: ${value.trim()}`); + const practiceByWeek = [8, 9].map((week) => { + const items = TECH.map((label, technique) => { + const days = DAYS.filter((_, day) => state.practice[`w${week}-p${technique}-d${day}`]); + return days.length ? `• ${label}: ${days.join(", ")}` : null; + }).filter(Boolean); + return { label: week === 8 ? 9 : 10, items }; + }); + const lines = [ + "Итоги плана за 9–10 недели", + "", + `Тренировки: ${done.length} из ${checks.length}`, + ...done.map((c) => `✓ ${workoutLabels[c.dataset.id]}`), + "", + "Комментарии:", + ...(comments.length ? comments : ["• Комментариев нет"]), + "", + "Практики саморегуляции:", + ]; + practiceByWeek.forEach(({ label, items }) => { + lines.push(`Неделя ${label}:`); + lines.push(...(items.length ? items : ["• Отметок нет"])); + }); + return lines.join("\n"); + } + async function copyReportText() { + const preview = document.getElementById("reportPreview"); + try { + await navigator.clipboard.writeText(preview.value); + } catch (_) { + preview.select(); + document.execCommand("copy"); + } + document.getElementById("copyStatus").textContent = "Текст скопирован"; + } renderWeek("week8Days", W8, 8); renderWeek("week9Days", W9, 9); document.querySelectorAll(".check").forEach( @@ -1195,6 +1276,18 @@ document .querySelectorAll("[data-game]") .forEach((g) => renderBingo(g, g.dataset.game)); + const reportDialog = document.getElementById("reportDialog"); + document.getElementById("openReport").onclick = () => { + document.getElementById("reportPreview").value = buildReport(); + document.getElementById("copyStatus").textContent = ""; + reportDialog.showModal(); + }; + document.getElementById("closeReport").onclick = () => reportDialog.close(); + document.getElementById("copyReport").onclick = copyReportText; + document.getElementById("sendReport").onclick = async () => { + await copyReportText(); + window.open("https://t.me/manoraya", "_blank", "noopener"); + }; updateProgress(); </script> </body>