Add config API and settings UI

GET/PUT /api/config for pay dates and amounts.
Settings page fetches and saves configuration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 19:04:33 -04:00
parent adebe10f52
commit 5f5f1111c5
3 changed files with 318 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ const express = require('express');
const cors = require('cors');
const path = require('path');
const healthRouter = require('./routes/health');
const configRouter = require('./routes/config');
const db = require('./db');
const app = express();
@@ -13,6 +14,7 @@ app.use(express.json());
// API routes
app.use('/api', healthRouter);
app.use('/api', configRouter);
// Serve static client files in production
const clientDist = path.join(__dirname, '../../client/dist');

View File

@@ -0,0 +1,82 @@
const express = require('express');
const router = express.Router();
const { pool } = require('../db');
const CONFIG_KEYS = [
'paycheck1_day',
'paycheck2_day',
'paycheck1_gross',
'paycheck1_net',
'paycheck2_gross',
'paycheck2_net',
];
const DEFAULTS = {
paycheck1_day: 1,
paycheck2_day: 15,
};
async function getAllConfig() {
const result = await pool.query(
'SELECT key, value FROM config WHERE key = ANY($1)',
[CONFIG_KEYS]
);
const map = {};
for (const row of result.rows) {
map[row.key] = row.value;
}
const config = {};
for (const key of CONFIG_KEYS) {
const raw = map[key] !== undefined ? map[key] : (DEFAULTS[key] !== undefined ? String(DEFAULTS[key]) : null);
config[key] = raw !== null ? Number(raw) : null;
}
return config;
}
router.get('/config', async (req, res) => {
try {
const config = await getAllConfig();
res.json(config);
} catch (err) {
console.error('GET /api/config error:', err);
res.status(500).json({ error: 'Failed to fetch config' });
}
});
router.put('/config', async (req, res) => {
try {
const updates = req.body;
const validKeys = Object.keys(updates).filter((k) => CONFIG_KEYS.includes(k));
if (validKeys.length > 0) {
const client = await pool.connect();
try {
await client.query('BEGIN');
for (const key of validKeys) {
await client.query(
`INSERT INTO config (key, value) VALUES ($1, $2)
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`,
[key, String(updates[key])]
);
}
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}
}
const config = await getAllConfig();
res.json(config);
} catch (err) {
console.error('PUT /api/config error:', err);
res.status(500).json({ error: 'Failed to update config' });
}
});
module.exports = router;