import { db } from '../db.js'; const ISO = /^\d{4}-\d{2}-\d{2}$/; function ensureDay(day) { db.prepare('INSERT OR IGNORE INTO training_days (day) VALUES (?)').run(day); } // Write an entry's metric values and bump any personal-best records they beat. // Pushes beaten records onto `newRecords`. Used by both create and update. function writeValues(entryId, values, newRecords) { if (!Array.isArray(values)) return; const ins = db.prepare('INSERT INTO entry_values (entry_id, metric_id, value) VALUES (?, ?, ?)'); for (const v of values) { if (!v || v.metric_id == null) continue; const metricId = Number(v.metric_id); const value = Number(v.value) || 0; ins.run(entryId, metricId, value); const m = db.prepare( 'SELECT id, name, record, higher_is_better, unit FROM category_metrics WHERE id = ? AND track_record = 1' ).get(metricId); if (m) { const beats = m.record == null || (m.higher_is_better ? value > m.record : value < m.record); if (beats) { db.prepare('UPDATE category_metrics SET record = ? WHERE id = ?').run(value, metricId); newRecords.push({ metric_id: m.id, name: m.name, unit: m.unit, value, previous: m.record }); } } } } function dayPayload(day) { const td = db.prepare('SELECT day, notes FROM training_days WHERE day = ?').get(day) || { day, notes: '' }; const entries = db.prepare( 'SELECT id, category_id, note, created_at FROM entries WHERE day = ? ORDER BY id' ).all(day); const valuesByEntry = new Map(); if (entries.length) { const ids = entries.map((e) => e.id); const rows = db.prepare( `SELECT entry_id, metric_id, value FROM entry_values WHERE entry_id IN (${ids.map(() => '?').join(',')})` ).all(...ids); for (const r of rows) { if (!valuesByEntry.has(r.entry_id)) valuesByEntry.set(r.entry_id, []); valuesByEntry.get(r.entry_id).push({ metric_id: r.metric_id, value: r.value }); } } const plans = db.prepare( 'SELECT category_id, note FROM plans WHERE day = ?' ).all(day); return { day: td.day, notes: td.notes, entries: entries.map((e) => ({ ...e, values: valuesByEntry.get(e.id) || [] })), plans, }; } export default async function entryRoutes(app) { app.get('/api/day/:day', async (req, reply) => { const { day } = req.params; if (!ISO.test(day)) return reply.code(400).send({ error: 'Bad date' }); return dayPayload(day); }); app.post('/api/entries', async (req, reply) => { const { day, category_id, values, note } = req.body || {}; if (!ISO.test(day || '')) return reply.code(400).send({ error: 'Bad date' }); const cat = db.prepare('SELECT id FROM categories WHERE id = ?').get(Number(category_id)); if (!cat) return reply.code(400).send({ error: 'Unknown category' }); const newRecords = []; const tx = db.transaction(() => { ensureDay(day); const { lastInsertRowid: entryId } = db.prepare( 'INSERT INTO entries (day, category_id, note) VALUES (?, ?, ?)' ).run(day, cat.id, String(note || '')); writeValues(entryId, values, newRecords); }); tx(); return { ...dayPayload(day), newRecords }; }); // Edit an existing entry: replace its values and note (and bump records). app.put('/api/entries/:id', async (req, reply) => { const id = Number(req.params.id); const entry = db.prepare('SELECT id, day FROM entries WHERE id = ?').get(id); if (!entry) return reply.code(404).send({ error: 'Not found' }); const { values, note } = req.body || {}; const newRecords = []; const tx = db.transaction(() => { if (note !== undefined) { db.prepare('UPDATE entries SET note = ? WHERE id = ?').run(String(note || ''), id); } db.prepare('DELETE FROM entry_values WHERE entry_id = ?').run(id); writeValues(id, values, newRecords); }); tx(); return { ...dayPayload(entry.day), newRecords }; }); app.delete('/api/entries/:id', async (req, reply) => { const id = Number(req.params.id); const row = db.prepare('SELECT day FROM entries WHERE id = ?').get(id); db.prepare('DELETE FROM entries WHERE id = ?').run(id); return reply.send(row ? dayPayload(row.day) : { ok: true }); }); app.put('/api/day/:day/notes', async (req, reply) => { const { day } = req.params; if (!ISO.test(day)) return reply.code(400).send({ error: 'Bad date' }); const notes = String((req.body && req.body.notes) || ''); ensureDay(day); db.prepare('UPDATE training_days SET notes = ? WHERE day = ?').run(notes, day); return { ok: true, notes }; }); }