← back to AbramsOS
routes/people.js
76 lines
// Household — people (self, spouse, dependents) whose records AbramsOS tracks.
const express = require('express');
const db = require('../lib/db');
const audit = require('../lib/audit');
const { id } = require('../lib/ids');
const router = express.Router();
const DEV_USER_ID = 'user_steve';
const RELATIONS = ['self', 'spouse', 'partner', 'child', 'dependent', 'parent', 'other'];
async function listPeople(userId) {
const r = await db.query(
`SELECT p.*,
(SELECT count(*)::int FROM medication m WHERE m.person_id = p.id AND m.is_active) AS med_count
FROM person p WHERE p.user_id = $1
ORDER BY (relation <> 'self'), full_name ASC`,
[userId]
);
return r.rows;
}
router.get('/household', async (_req, res) => {
res.render('household', { people: await listPeople(DEV_USER_ID), relations: RELATIONS });
});
router.get('/api/people', async (_req, res) => {
res.json(await listPeople(DEV_USER_ID));
});
router.post('/api/people', async (req, res) => {
try {
const b = req.body || {};
if (!b.full_name) return res.status(400).json({ error: 'full_name required' });
const pid = id('person');
await db.query(
`INSERT INTO person (id, user_id, relation, full_name, nickname, dob, phone, email, notes)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
[pid, DEV_USER_ID, RELATIONS.includes(b.relation) ? b.relation : 'other', b.full_name,
b.nickname || null, b.dob || null, b.phone || null, b.email || null, b.notes || null]
);
await audit.log({ actorType: 'user', actorId: DEV_USER_ID, objectType: 'person', objectId: pid, eventType: 'person_created', metadata: { relation: b.relation } });
const r = await db.query(`SELECT * FROM person WHERE id = $1`, [pid]);
res.status(201).json(r.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.post('/api/people/:id', async (req, res) => {
try {
const b = req.body || {};
const fields = ['relation', 'full_name', 'nickname', 'dob', 'phone', 'email', 'notes'];
const sets = [], vals = [];
let i = 1;
for (const f of fields) { if (b[f] === undefined) continue; sets.push(`${f} = $${i++}`); vals.push(b[f] === '' ? null : b[f]); }
if (!sets.length) return res.status(400).json({ error: 'no fields to update' });
sets.push(`updated_at = now()`);
vals.push(req.params.id, DEV_USER_ID);
const r = await db.query(`UPDATE person SET ${sets.join(', ')} WHERE id = $${i++} AND user_id = $${i} RETURNING *`, vals);
if (!r.rows.length) return res.status(404).json({ error: 'not found' });
await audit.log({ actorType: 'user', actorId: DEV_USER_ID, objectType: 'person', objectId: req.params.id, eventType: 'person_updated' });
res.json(r.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.post('/api/people/:id/delete', async (req, res) => {
await db.query(`DELETE FROM person WHERE id = $1 AND user_id = $2`, [req.params.id, DEV_USER_ID]);
await audit.log({ actorType: 'user', actorId: DEV_USER_ID, objectType: 'person', objectId: req.params.id, eventType: 'person_deleted' });
res.json({ ok: true });
});
module.exports = router;