Filtering bad
This commit is contained in:
+1
-1
@@ -23,4 +23,4 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
jwl.tar.gz
|
||||
/.jwl
|
||||
/jwl
|
||||
+99
-51
@@ -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<void> {
|
||||
showBadOnly = false;
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="history-header">
|
||||
<button id="btnHistoryBack" class="btn" style="flex:none; padding:0.4rem 0.8rem;">← Назад</button>
|
||||
<span style="font-size:0.85rem;color:var(--text);">Загрузка...</span>
|
||||
</div>
|
||||
<div class="history-filters">
|
||||
<button id="btnFilterAll" class="btn btn-filter active">📊 Все записи</button>
|
||||
<button id="btnFilterBad" class="btn btn-filter">🔴 Только плохие</button>
|
||||
</div>
|
||||
<div class="history-card">
|
||||
<div id="history-content" style="text-align:center;padding:2rem;opacity:0.6;">Загрузка истории...</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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 = '<div style="text-align:center;padding:2rem;opacity:0.6;">История пуста</div>';
|
||||
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<string, string> = {
|
||||
easy: '🟢 Легко',
|
||||
ok: '🟡 Нормально',
|
||||
hard: '🟠 Тяжело',
|
||||
none: '🔴 Не знаю',
|
||||
};
|
||||
|
||||
const row = document.createElement('div');
|
||||
row.className = 'history-row';
|
||||
row.innerHTML = `
|
||||
<div class="history-row-word">${escapeHtml(wordText)}</div>
|
||||
<div class="history-row-mode">${modeLabels[record.mode] || record.mode}</div>
|
||||
<div class="history-row-scores">
|
||||
<span class="history-score-before">${record.score_before.toFixed(1)}</span>
|
||||
<span class="history-score-arrow">→</span>
|
||||
<span class="history-score-after">${record.score_after.toFixed(1)}</span>
|
||||
</div>
|
||||
<div class="history-row-date">${dateStr}</div>
|
||||
`;
|
||||
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<number, string> || 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 = '<div style="text-align:center;padding:2rem;opacity:0.6;">Нет записей</div>';
|
||||
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<string, string> = {
|
||||
easy: '🟢 Легко',
|
||||
ok: '🟡 Нормально',
|
||||
hard: '🟠 Тяжело',
|
||||
none: '🔴 Не знаю',
|
||||
};
|
||||
|
||||
const row = document.createElement('div');
|
||||
row.className = 'history-row';
|
||||
row.innerHTML = `
|
||||
<div class="history-row-word">${escapeHtml(wordText)}</div>
|
||||
<div class="history-row-mode">${modeLabels[record.mode] || record.mode}</div>
|
||||
<div class="history-row-scores">
|
||||
<span class="history-score-before">${record.score_before.toFixed(1)}</span>
|
||||
<span class="history-score-arrow">→</span>
|
||||
<span class="history-score-after">${record.score_after.toFixed(1)}</span>
|
||||
</div>
|
||||
<div class="history-row-date">${dateStr}</div>
|
||||
`;
|
||||
contentEl.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
async function getWordMap(wordIds: number[]): Promise<Map<number, string>> {
|
||||
const map = new Map<number, string>();
|
||||
if (wordIds.length === 0) return map;
|
||||
@@ -104,7 +153,6 @@ async function getWordMap(wordIds: number[]): Promise<Map<number, string>> {
|
||||
wordIds,
|
||||
);
|
||||
for (const row of rows) {
|
||||
// rows — массив массивов [id, key]
|
||||
map.set(row[0], row[1]);
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user