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:
@@ -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');
|
||||
|
||||
82
server/src/routes/config.js
Normal file
82
server/src/routes/config.js
Normal 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;
|
||||
Reference in New Issue
Block a user