88 occurrences of res.status(500).json({ error: e.message }) (or
err.message) swept to generic 'Request failed'. Server-side
console.error / logger.error calls are untouched, so the full detail
still lands in logs and Grafana.
Covers: admin, adminConfig, adminMilestones, chartReview, documents,
encounters, hospitalCourse, hpi, learningAdmin, learningAI, learningHub,
logs, memories, milestones, oidc, refine, sessions, sickVisit, soap,
userPreferences, wellVisit.
Also extends .gitignore to exclude .env.backup-* files.
149 lines
4.3 KiB
JavaScript
149 lines
4.3 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const { adminMiddleware } = require('../middleware/auth');
|
|
const db = require('../db/database');
|
|
|
|
// Get all milestones (optionally filtered by age group)
|
|
router.get('/milestones', adminMiddleware, async (req, res) => {
|
|
try {
|
|
const { age_group } = req.query;
|
|
let query = 'SELECT * FROM developmental_milestones';
|
|
let params = [];
|
|
|
|
if (age_group) {
|
|
query += ' WHERE age_group = $1';
|
|
params.push(age_group);
|
|
}
|
|
|
|
query += ' ORDER BY age_group, domain, sort_order, id';
|
|
|
|
const result = await db.query(query, params);
|
|
res.json({ success: true, milestones: result.rows });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Request failed' });
|
|
}
|
|
});
|
|
|
|
// Get unique age groups and domains
|
|
router.get('/milestones/meta', adminMiddleware, async (req, res) => {
|
|
try {
|
|
const ageGroups = await db.query(
|
|
'SELECT DISTINCT age_group FROM developmental_milestones ORDER BY age_group'
|
|
);
|
|
const domains = await db.query(
|
|
'SELECT DISTINCT domain FROM developmental_milestones ORDER BY domain'
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
age_groups: ageGroups.rows.map(r => r.age_group),
|
|
domains: domains.rows.map(r => r.domain)
|
|
});
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Request failed' });
|
|
}
|
|
});
|
|
|
|
// Create new milestone
|
|
router.post('/milestones', adminMiddleware, async (req, res) => {
|
|
try {
|
|
const { age_group, domain, milestone_text, sort_order } = req.body;
|
|
|
|
if (!age_group || !domain || !milestone_text) {
|
|
return res.status(400).json({ error: 'Missing required fields' });
|
|
}
|
|
|
|
const result = await db.query(
|
|
`INSERT INTO developmental_milestones (age_group, domain, milestone_text, sort_order)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING *`,
|
|
[age_group, domain, milestone_text, sort_order || 0]
|
|
);
|
|
|
|
res.json({ success: true, milestone: result.rows[0] });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Request failed' });
|
|
}
|
|
});
|
|
|
|
// Update milestone
|
|
router.put('/milestones/:id', adminMiddleware, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { age_group, domain, milestone_text, sort_order } = req.body;
|
|
|
|
if (!age_group || !domain || !milestone_text) {
|
|
return res.status(400).json({ error: 'Missing required fields' });
|
|
}
|
|
|
|
const result = await db.query(
|
|
`UPDATE developmental_milestones
|
|
SET age_group = $1, domain = $2, milestone_text = $3, sort_order = $4, updated_at = NOW()
|
|
WHERE id = $5
|
|
RETURNING *`,
|
|
[age_group, domain, milestone_text, sort_order || 0, id]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Milestone not found' });
|
|
}
|
|
|
|
res.json({ success: true, milestone: result.rows[0] });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Request failed' });
|
|
}
|
|
});
|
|
|
|
// Delete milestone
|
|
router.delete('/milestones/:id', adminMiddleware, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const result = await db.query(
|
|
'DELETE FROM developmental_milestones WHERE id = $1 RETURNING *',
|
|
[id]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Milestone not found' });
|
|
}
|
|
|
|
res.json({ success: true, message: 'Milestone deleted' });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Request failed' });
|
|
}
|
|
});
|
|
|
|
// Bulk import milestones (from static data)
|
|
router.post('/milestones/bulk-import', adminMiddleware, async (req, res) => {
|
|
try {
|
|
const { milestones, clearExisting } = req.body;
|
|
|
|
if (!Array.isArray(milestones)) {
|
|
return res.status(400).json({ error: 'Expected array of milestones' });
|
|
}
|
|
|
|
// Clear existing if requested
|
|
if (clearExisting) {
|
|
await db.query('DELETE FROM developmental_milestones');
|
|
}
|
|
|
|
let imported = 0;
|
|
for (const m of milestones) {
|
|
if (!m.age_group || !m.domain || !m.milestone_text) continue;
|
|
|
|
await db.query(
|
|
`INSERT INTO developmental_milestones (age_group, domain, milestone_text, sort_order)
|
|
VALUES ($1, $2, $3, $4)`,
|
|
[m.age_group, m.domain, m.milestone_text, m.sort_order || 0]
|
|
);
|
|
imported++;
|
|
}
|
|
|
|
res.json({ success: true, imported });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Request failed' });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|