history page works
This commit is contained in:
@@ -79,9 +79,7 @@ impl CardStatistics {
|
|||||||
self.score = MAX_SCORE
|
self.score = MAX_SCORE
|
||||||
}
|
}
|
||||||
self.last_open = Utc::now();
|
self.last_open = Utc::now();
|
||||||
updateCardStat(self.id, self.score, last_open).catch((err) =>
|
}
|
||||||
console.error('[Training] Ошибка сохранения last_opened:', err),
|
|
||||||
); }
|
|
||||||
|
|
||||||
pub fn calculated_score(&self) -> f32 {
|
pub fn calculated_score(&self) -> f32 {
|
||||||
let time = Utc::now() - self.last_open;
|
let time = Utc::now() - self.last_open;
|
||||||
|
|||||||
@@ -55,9 +55,7 @@ export function calculatedScore(stat: CardStatistics): number {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const diffMs = now - stat.last_open;
|
const diffMs = now - stat.last_open;
|
||||||
const days = diffMs / (1000 * 60 * 60 * 24);
|
const days = diffMs / (1000 * 60 * 60 * 24);
|
||||||
console.log(`Days ${days}`)
|
|
||||||
const multiplier = Math.pow(FADE_PER_DAY, Math.max(0, days));
|
const multiplier = Math.pow(FADE_PER_DAY, Math.max(0, days));
|
||||||
console.log(`multiplier ${multiplier}`)
|
|
||||||
const v = stat.score * multiplier;
|
const v = stat.score * multiplier;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,4 @@
|
|||||||
import { getHistory } from './history.ts';
|
import { getHistory } from './history.ts';
|
||||||
import type { RepetitionRecord } from './history.ts';
|
|
||||||
import { execute } from './sqlite-manager.ts';
|
import { execute } from './sqlite-manager.ts';
|
||||||
|
|
||||||
export interface HistoryViewCallbacks {
|
export interface HistoryViewCallbacks {
|
||||||
@@ -24,12 +23,15 @@ export async function renderHistoryView(
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
console.log('[History] Загрузка истории для set_id:', setId);
|
||||||
|
|
||||||
document.getElementById('btnHistoryBack')?.addEventListener('click', () => {
|
document.getElementById('btnHistoryBack')?.addEventListener('click', () => {
|
||||||
callbacks.onBack();
|
callbacks.onBack();
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const records = await getHistory(setId, 2000);
|
const records = await getHistory(setId, 2000);
|
||||||
|
console.log('[History] Получено записей:', records.length, records);
|
||||||
|
|
||||||
// Получаем слова для отображения
|
// Получаем слова для отображения
|
||||||
const wordIds = [...new Set(records.map((r) => r.word_id))];
|
const wordIds = [...new Set(records.map((r) => r.word_id))];
|
||||||
|
|||||||
+27
@@ -237,6 +237,8 @@ function showSetDetail(set: CardSetRecord, listItem: HTMLLIElement) {
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<button class="btn btn-primary btn-start-training" data-set-id="${set.id}">▶ Начать тренировку</button>
|
<button class="btn btn-primary btn-start-training" data-set-id="${set.id}">▶ Начать тренировку</button>
|
||||||
|
|
||||||
|
<button class="btn btn-history" data-set-id="${set.id}" style="width:100%;margin-top:8px;">📊 История повторений</button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +260,24 @@ function showWordView(word: Word) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- История -----
|
||||||
|
|
||||||
|
async function showHistory(setId: number) {
|
||||||
|
hideNav();
|
||||||
|
currentPage = 'history';
|
||||||
|
|
||||||
|
await renderHistoryView(
|
||||||
|
container,
|
||||||
|
setId,
|
||||||
|
{
|
||||||
|
onBack: () => {
|
||||||
|
showNav();
|
||||||
|
switchPage('sets');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ----- Тренировка -----
|
// ----- Тренировка -----
|
||||||
|
|
||||||
async function startTraining(setId: number) {
|
async function startTraining(setId: number) {
|
||||||
@@ -453,6 +473,13 @@ async function main() {
|
|||||||
const setId = Number((trainingBtn as HTMLElement).dataset.setId);
|
const setId = Number((trainingBtn as HTMLElement).dataset.setId);
|
||||||
await startTraining(setId);
|
await startTraining(setId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const historyBtn = target.closest('.btn-history');
|
||||||
|
if (historyBtn) {
|
||||||
|
const setId = Number((historyBtn as HTMLElement).dataset.setId);
|
||||||
|
console.log('[App] История для set_id:', setId);
|
||||||
|
await showHistory(setId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -417,6 +417,86 @@ body {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ──── История ──── */
|
||||||
|
|
||||||
|
.history-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-card {
|
||||||
|
flex: 1;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#history-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row-word {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-h);
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row-mode {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row-scores {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-score-before {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-score-arrow {
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-score-after {
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row-date {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.7;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ──── Навигация ──── */
|
/* ──── Навигация ──── */
|
||||||
|
|
||||||
.app-nav {
|
.app-nav {
|
||||||
|
|||||||
+6
-3
@@ -244,15 +244,18 @@ export class TrainingPage {
|
|||||||
if (stat) {
|
if (stat) {
|
||||||
const scoreAfter = Math.round(calculatedScore(stat) * 10) / 10;
|
const scoreAfter = Math.round(calculatedScore(stat) * 10) / 10;
|
||||||
const word = this.words[current];
|
const word = this.words[current];
|
||||||
|
console.log('[Training] vote debug:', { stat, word, current, scoreBefore, scoreAfter });
|
||||||
if (word) {
|
if (word) {
|
||||||
addRepetition({
|
const record = {
|
||||||
set_id: stat.set_id,
|
set_id: Number(stat.set_id),
|
||||||
word_id: word.id,
|
word_id: word.id,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
mode,
|
mode,
|
||||||
score_before: scoreBefore,
|
score_before: scoreBefore,
|
||||||
score_after: scoreAfter,
|
score_after: scoreAfter,
|
||||||
}).catch((err) => console.error('[Training] Ошибка сохранения истории:', err));
|
};
|
||||||
|
console.log('[Training] Сохраняем историю:', record);
|
||||||
|
addRepetition(record).catch((err) => console.error('[Training] Ошибка сохранения истории:', err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user