From 064c39924b50904ee5ab66f3737a6e9c11f35fb8 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Sun, 5 Jul 2026 13:34:23 +0300 Subject: [PATCH] Filtering bad --- .gitignore | 2 +- src/history-view.ts | 150 +++++++++++++++++++++++++++++--------------- src/style.css | 19 ++++++ 3 files changed, 119 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 82665a7..e4041a2 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ dist-ssr *.sln *.sw? jwl.tar.gz -/.jwl \ No newline at end of file +/jwl \ No newline at end of file diff --git a/src/history-view.ts b/src/history-view.ts index bab98d8..ca4284b 100644 --- a/src/history-view.ts +++ b/src/history-view.ts @@ -5,6 +5,11 @@ export interface HistoryViewCallbacks { onBack: () => void; } +let allRecords: any[] = []; +let showBadOnly = false; + +const BAD_MODES = new Set(['hard', 'none']); + /** * Показывает страницу истории повторений для указанной колоды. */ @@ -13,77 +18,53 @@ export async function renderHistoryView( setId: number, callbacks: HistoryViewCallbacks, ): Promise { + showBadOnly = false; + container.innerHTML = `
Загрузка...
+
+ + +
Загрузка истории...
`; - console.log('[History] Загрузка истории для set_id:', setId); - document.getElementById('btnHistoryBack')?.addEventListener('click', () => { callbacks.onBack(); }); - try { - const records = await getHistory(setId, 2000); - console.log('[History] Получено записей:', records.length, records); + document.getElementById('btnFilterAll')?.addEventListener('click', () => { + showBadOnly = false; + updateFilterButtons(); + renderRows(container); + }); - // Получаем слова для отображения - const wordIds = [...new Set(records.map((r) => r.word_id))]; + document.getElementById('btnFilterBad')?.addEventListener('click', () => { + showBadOnly = true; + updateFilterButtons(); + renderRows(container); + }); + + try { + allRecords = await getHistory(setId, 2000); + + const wordIds = [...new Set(allRecords.map((r) => r.word_id))]; const wordMap = await getWordMap(wordIds); + // Сохраняем wordMap для renderRows + (window as any).__wordMap = wordMap; + const headerSpan = container.querySelector('.history-header span'); if (headerSpan) { - headerSpan.textContent = `записей: ${records.length}`; + headerSpan.textContent = `всего записей: ${allRecords.length}`; } - const contentEl = document.getElementById('history-content'); - if (!contentEl) return; - - if (records.length === 0) { - contentEl.innerHTML = '
История пуста
'; - return; - } - - contentEl.innerHTML = ''; - - for (const record of records) { - const word = wordMap.get(record.word_id); - const wordText = word ? word : `#${record.word_id}`; - const date = new Date(record.timestamp); - const dateStr = date.toLocaleString('ru-RU', { - day: '2-digit', - month: '2-digit', - hour: '2-digit', - minute: '2-digit', - }); - - const modeLabels: Record = { - easy: '🟢 Легко', - ok: '🟡 Нормально', - hard: '🟠 Тяжело', - none: '🔴 Не знаю', - }; - - const row = document.createElement('div'); - row.className = 'history-row'; - row.innerHTML = ` -
${escapeHtml(wordText)}
-
${modeLabels[record.mode] || record.mode}
-
- ${record.score_before.toFixed(1)} - - ${record.score_after.toFixed(1)} -
-
${dateStr}
- `; - contentEl.appendChild(row); - } + renderRows(container); } catch (err) { console.error('[History] Ошибка загрузки:', err); const contentEl = document.getElementById('history-content'); @@ -93,6 +74,74 @@ export async function renderHistoryView( } } +function updateFilterButtons() { + const btnAll = document.getElementById('btnFilterAll'); + const btnBad = document.getElementById('btnFilterBad'); + if (btnAll && btnBad) { + btnAll.classList.toggle('active', !showBadOnly); + btnBad.classList.toggle('active', showBadOnly); + } +} + +function renderRows(container: HTMLElement) { + const contentEl = document.getElementById('history-content'); + if (!contentEl) return; + + const wordMap = (window as any).__wordMap as Map || new Map(); + const headerSpan = container.querySelector('.history-header span'); + + let filtered = allRecords; + if (showBadOnly) { + filtered = allRecords.filter((r) => BAD_MODES.has(r.mode)); + } + + if (headerSpan) { + headerSpan.textContent = showBadOnly + ? `плохих: ${filtered.length} / ${allRecords.length}` + : `всего записей: ${allRecords.length}`; + } + + if (filtered.length === 0) { + contentEl.innerHTML = '
Нет записей
'; + return; + } + + contentEl.innerHTML = ''; + + for (const record of filtered) { + const word = wordMap.get(record.word_id); + const wordText = word ? word : `#${record.word_id}`; + const date = new Date(record.timestamp); + const dateStr = date.toLocaleString('ru-RU', { + day: '2-digit', + month: '2-digit', + hour: '2-digit', + minute: '2-digit', + }); + + const modeLabels: Record = { + easy: '🟢 Легко', + ok: '🟡 Нормально', + hard: '🟠 Тяжело', + none: '🔴 Не знаю', + }; + + const row = document.createElement('div'); + row.className = 'history-row'; + row.innerHTML = ` +
${escapeHtml(wordText)}
+
${modeLabels[record.mode] || record.mode}
+
+ ${record.score_before.toFixed(1)} + + ${record.score_after.toFixed(1)} +
+
${dateStr}
+ `; + contentEl.appendChild(row); + } +} + async function getWordMap(wordIds: number[]): Promise> { const map = new Map(); if (wordIds.length === 0) return map; @@ -104,7 +153,6 @@ async function getWordMap(wordIds: number[]): Promise> { wordIds, ); for (const row of rows) { - // rows — массив массивов [id, key] map.set(row[0], row[1]); } } catch { diff --git a/src/style.css b/src/style.css index 51cfe21..431d407 100644 --- a/src/style.css +++ b/src/style.css @@ -426,6 +426,25 @@ body { padding: 0.5rem 0; } +.history-filters { + display: flex; + gap: 0.5rem; + padding: 0.25rem 0; +} + +.btn-filter { + flex: 1; + font-size: 0.8rem; + padding: 0.5rem 0.75rem; + min-height: 40px; +} + +.btn-filter.active { + background: var(--accent-light); + border-color: var(--accent); + color: var(--accent); +} + .history-card { flex: 1; background: var(--card-bg);