- Convert monster_catches from table to view for automatic calculation - Add overwrite checkbox to monster import UI - Remove manual INSERT/UPDATE logic for catches (now handled by view) - Simplify API endpoints to query view instead of managing state - Add confirmation dialog for overwrite operation Benefits: - No data duplication - Always accurate catch status based on current steps - Simpler codebase with less state management - Easier to reset steps without orphaned catch records 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
123 lines
3.6 KiB
JavaScript
123 lines
3.6 KiB
JavaScript
const sqlite3 = require('sqlite3').verbose();
|
|
const path = require('path');
|
|
|
|
const dbPath = path.join(__dirname, '..', 'step_competition.db');
|
|
const db = new sqlite3.Database(dbPath);
|
|
|
|
// Initialize database schema
|
|
function initializeDatabase() {
|
|
db.serialize(() => {
|
|
// Teams table
|
|
db.run(`
|
|
CREATE TABLE IF NOT EXISTS teams (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`);
|
|
|
|
// Participants table
|
|
db.run(`
|
|
CREATE TABLE IF NOT EXISTS participants (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
email TEXT UNIQUE,
|
|
team_id INTEGER NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (team_id) REFERENCES teams(id)
|
|
)
|
|
`);
|
|
|
|
// Daily steps table
|
|
db.run(`
|
|
CREATE TABLE IF NOT EXISTS daily_steps (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
participant_id INTEGER NOT NULL,
|
|
date DATE NOT NULL,
|
|
steps INTEGER NOT NULL DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (participant_id) REFERENCES participants(id),
|
|
UNIQUE(participant_id, date)
|
|
)
|
|
`);
|
|
|
|
// Daily monsters table
|
|
db.run(`
|
|
CREATE TABLE IF NOT EXISTS daily_monsters (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
date DATE NOT NULL UNIQUE,
|
|
monster_name TEXT NOT NULL,
|
|
monster_description TEXT,
|
|
step_goal INTEGER NOT NULL,
|
|
monster_icon TEXT DEFAULT '👹',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`);
|
|
|
|
// Monster catches view - derived from actual step data
|
|
// Drop the old table if it exists and recreate as a view
|
|
db.run(`DROP TABLE IF EXISTS monster_catches`);
|
|
db.run(`
|
|
CREATE VIEW IF NOT EXISTS monster_catches AS
|
|
SELECT
|
|
dm.id as monster_id,
|
|
dm.date,
|
|
t.id as team_id,
|
|
t.name as team_name,
|
|
COALESCE(SUM(ds.steps), 0) as final_steps,
|
|
dm.step_goal
|
|
FROM daily_monsters dm
|
|
CROSS JOIN teams t
|
|
LEFT JOIN participants p ON t.id = p.team_id
|
|
LEFT JOIN daily_steps ds ON p.id = ds.participant_id AND ds.date = dm.date
|
|
GROUP BY dm.id, dm.date, t.id, t.name, dm.step_goal
|
|
HAVING final_steps >= dm.step_goal
|
|
`);
|
|
|
|
// Create indexes for better query performance
|
|
db.run(`CREATE INDEX IF NOT EXISTS idx_daily_steps_date ON daily_steps(date)`);
|
|
db.run(`CREATE INDEX IF NOT EXISTS idx_daily_steps_participant ON daily_steps(participant_id)`);
|
|
db.run(`CREATE INDEX IF NOT EXISTS idx_participants_team ON participants(team_id)`);
|
|
db.run(`CREATE INDEX IF NOT EXISTS idx_daily_monsters_date ON daily_monsters(date)`);
|
|
|
|
console.log('Database initialized successfully');
|
|
});
|
|
}
|
|
|
|
// Helper function to run queries with promises
|
|
function runQuery(sql, params = []) {
|
|
return new Promise((resolve, reject) => {
|
|
db.run(sql, params, function(err) {
|
|
if (err) reject(err);
|
|
else resolve({ id: this.lastID, changes: this.changes });
|
|
});
|
|
});
|
|
}
|
|
|
|
function getQuery(sql, params = []) {
|
|
return new Promise((resolve, reject) => {
|
|
db.get(sql, params, (err, row) => {
|
|
if (err) reject(err);
|
|
else resolve(row);
|
|
});
|
|
});
|
|
}
|
|
|
|
function allQuery(sql, params = []) {
|
|
return new Promise((resolve, reject) => {
|
|
db.all(sql, params, (err, rows) => {
|
|
if (err) reject(err);
|
|
else resolve(rows);
|
|
});
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
db,
|
|
initializeDatabase,
|
|
runQuery,
|
|
getQuery,
|
|
allQuery
|
|
};
|