Add records, Elijah scores, per-session notes, and PWA update prompt
App features: - Personal-best records per metric: manually settable in Settings and auto-updated when a session beats them; shown in the log modal and a new dashboard "Personal records" card. - Juggling now counts by 1 instead of 5. - 1-on-1 with Elijah gains Technical Skill and Effort scores (out of 10) as manual inputs, plus an optional per-session note. - Service worker now uses a controlled update flow: an in-app "new version ready" banner activates the update on tap and reloads. Data model: - category_metrics gains track_record + record; entries gains note. - Idempotent migrations bring existing databases up to date (juggling step/record, Elijah score metrics) alongside the updated seed. StartOS package: - Bump to 0.1.2:0 with release notes. - Build x86_64 only (drop aarch64) per deployment target.
This commit is contained in:
+23
-6
@@ -10,7 +10,7 @@ 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, created_at FROM entries WHERE day = ? ORDER BY id'
|
||||
'SELECT id, category_id, note, created_at FROM entries WHERE day = ? ORDER BY id'
|
||||
).all(day);
|
||||
const valuesByEntry = new Map();
|
||||
if (entries.length) {
|
||||
@@ -42,26 +42,43 @@ export default async function entryRoutes(app) {
|
||||
});
|
||||
|
||||
app.post('/api/entries', async (req, reply) => {
|
||||
const { day, category_id, values } = req.body || {};
|
||||
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) VALUES (?, ?)'
|
||||
).run(day, cat.id);
|
||||
'INSERT INTO entries (day, category_id, note) VALUES (?, ?, ?)'
|
||||
).run(day, cat.id, String(note || ''));
|
||||
if (Array.isArray(values)) {
|
||||
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) ins.run(entryId, Number(v.metric_id), Number(v.value) || 0);
|
||||
if (!v || v.metric_id == null) continue;
|
||||
const metricId = Number(v.metric_id);
|
||||
const value = Number(v.value) || 0;
|
||||
ins.run(entryId, metricId, value);
|
||||
|
||||
// Auto-update personal-best records.
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return entryId;
|
||||
});
|
||||
tx();
|
||||
return dayPayload(day);
|
||||
return { ...dayPayload(day), newRecords };
|
||||
});
|
||||
|
||||
app.delete('/api/entries/:id', async (req, reply) => {
|
||||
|
||||
Reference in New Issue
Block a user