diff --git a/server/db.js b/server/db.js index 5b7070a..202da97 100644 --- a/server/db.js +++ b/server/db.js @@ -73,7 +73,8 @@ function initDb(dbPath) { } function updateProfileSeen(id) { - instance.prepare('UPDATE profiles SET last_seen = ? WHERE id = ?').run(Date.now(), id) + const result = instance.prepare('UPDATE profiles SET last_seen = ? WHERE id = ?').run(Date.now(), id) + if (result.changes === 0) console.warn(`[db] updateProfileSeen: no profile id=${id}`) } function createMagicToken(token, profileId, expiresAt) { @@ -128,7 +129,7 @@ function initDb(dbPath) { } function recordAnswer(profileId, letter, correct) { - letter = letter.toUpperCase() + const normalizedLetter = letter.toUpperCase() const progress = getOrCreateProgress(profileId) const now = Date.now() const isFirstToday = !progress.last_session_at || @@ -156,15 +157,15 @@ function initDb(dbPath) { // Update letter stats const existing = instance .prepare('SELECT * FROM letter_stats WHERE profile_id = ? AND letter = ?') - .get(profileId, letter) + .get(profileId, normalizedLetter) if (existing) { instance.prepare( 'UPDATE letter_stats SET correct = correct + ?, attempts = attempts + 1, updated_at = ? WHERE profile_id = ? AND letter = ?' - ).run(correct ? 1 : 0, now, profileId, letter) + ).run(correct ? 1 : 0, now, profileId, normalizedLetter) } else { instance.prepare( 'INSERT INTO letter_stats (profile_id, letter, correct, attempts, updated_at) VALUES (?,?,?,1,?)' - ).run(profileId, letter, correct ? 1 : 0, now) + ).run(profileId, normalizedLetter, correct ? 1 : 0, now) } return instance.prepare('SELECT * FROM user_progress WHERE profile_id = ?').get(profileId) @@ -181,17 +182,17 @@ function initDb(dbPath) { const now = Date.now() const upperLetter = letter.toUpperCase() - instance.prepare( - `INSERT INTO user_mnemonics (profile_id, letter, phrase, created_at) VALUES (?,?,?,?) - ON CONFLICT(profile_id, letter) DO UPDATE SET phrase = excluded.phrase` + // Try insert first — changes=1 means new row (first save), changes=0 means update path + const insertResult = instance.prepare( + 'INSERT OR IGNORE INTO user_mnemonics (profile_id, letter, phrase, created_at) VALUES (?,?,?,?)' ).run(profileId, upperLetter, phrase, now) - // Only award 50 pts on first save — if created_at = now, it was just inserted - const row = instance.prepare( - 'SELECT created_at FROM user_mnemonics WHERE profile_id = ? AND letter = ?' - ).get(profileId, upperLetter) - - if (row.created_at === now) { + if (insertResult.changes === 0) { + // Existing mnemonic — update phrase only, no point award + instance.prepare('UPDATE user_mnemonics SET phrase = ? WHERE profile_id = ? AND letter = ?') + .run(phrase, profileId, upperLetter) + } else { + // First save — award 50 points getOrCreateProgress(profileId) instance.prepare('UPDATE user_progress SET score = score + 50, updated_at = ? WHERE profile_id = ?') .run(now, profileId) @@ -218,7 +219,7 @@ function initDb(dbPath) { function getAdminStats() { const totalUsers = instance.prepare('SELECT COUNT(*) as c FROM profiles').get().c - const avgScore = instance.prepare('SELECT AVG(score) as a FROM user_progress').get().a || 0 + const avgScore = instance.prepare('SELECT AVG(score) as a FROM user_progress').get().a ?? 0 const mostMissed = instance.prepare(` SELECT letter, SUM(attempts - correct) as misses FROM letter_stats GROUP BY letter ORDER BY misses DESC LIMIT 5 diff --git a/server/tests/db.test.js b/server/tests/db.test.js index 3893332..6445581 100644 --- a/server/tests/db.test.js +++ b/server/tests/db.test.js @@ -159,6 +159,17 @@ describe('mnemonics', () => { assert.equal(m.A, 'go WALKING') assert.equal(m.E, 'go') }) + + test('saveMnemonic awards 50 pts on first save, not on update', () => { + const p1 = db.getOrCreateProgress(profileId) + db.saveMnemonic(profileId, 'T', 'dash') // first save → +50 pts + const p2 = db.getOrCreateProgress(profileId) + assert.equal(p2.score, p1.score + 50) + + db.saveMnemonic(profileId, 'T', 'updated dash') // update → no extra pts + const p3 = db.getOrCreateProgress(profileId) + assert.equal(p3.score, p2.score) + }) }) describe('letter stats', () => {