← back to Dw Boardroom Governance

src/api/routes/initiatives.ts

110 lines

import { Router, Request, Response } from 'express';
import { query, queryOne, execute } from '../../db/db';
import { Initiative } from '../../types';
import { v4 as uuidv4 } from 'uuid';

const router = Router();

// GET /api/initiatives
router.get('/', async (_req: Request, res: Response) => {
  try {
    const initiatives = await query<Initiative>(
      'SELECT * FROM gov_initiative_registry ORDER BY created_at DESC LIMIT 50'
    );
    res.json(initiatives);
  } catch (err: any) {
    res.status(500).json({ error: err.message });
  }
});

// POST /api/initiatives
router.post('/', async (req: Request, res: Response) => {
  try {
    const id = uuidv4();
    await query(
      `INSERT INTO gov_initiative_registry
       (id, title, description, driver_agent, kpi, risk_level, capital_allocated, status)
       VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
      [
        id,
        req.body.title,
        req.body.description || null,
        req.body.driverAgent || 'unassigned',
        req.body.kpi || null,
        req.body.riskLevel || 'low',
        req.body.capitalAllocated || 0,
        req.body.status || 'proposed',
      ]
    );
    const initiative = await queryOne<Initiative>(
      'SELECT * FROM gov_initiative_registry WHERE id = $1', [id]
    );
    res.json(initiative);
  } catch (err: any) {
    res.status(400).json({ error: err.message });
  }
});

// GET /api/initiatives/:id
router.get('/:id', async (req: Request, res: Response) => {
  try {
    const initiative = await queryOne<Initiative>(
      'SELECT * FROM gov_initiative_registry WHERE id = $1', [req.params.id]
    );
    if (!initiative) return res.status(404).json({ error: 'Initiative not found' });
    res.json(initiative);
  } catch (err: any) {
    res.status(500).json({ error: err.message });
  }
});

// PATCH /api/initiatives/:id
router.patch('/:id', async (req: Request, res: Response) => {
  try {
    const fields: string[] = [];
    const values: any[] = [];
    let idx = 1;

    const allowedFields = ['title', 'description', 'driver_agent', 'kpi', 'risk_level', 'capital_allocated', 'status', 'progress'];
    for (const field of allowedFields) {
      const camelKey = field.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
      if (req.body[camelKey] !== undefined || req.body[field] !== undefined) {
        fields.push(`${field} = $${idx++}`);
        values.push(req.body[camelKey] ?? req.body[field]);
      }
    }

    if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' });

    fields.push(`updated_at = NOW()`);
    values.push(req.params.id);

    await execute(
      `UPDATE gov_initiative_registry SET ${fields.join(', ')} WHERE id = $${idx}`,
      values
    );

    const initiative = await queryOne<Initiative>(
      'SELECT * FROM gov_initiative_registry WHERE id = $1', [req.params.id]
    );
    res.json(initiative);
  } catch (err: any) {
    res.status(400).json({ error: err.message });
  }
});

// DELETE /api/initiatives/:id
router.delete('/:id', async (req: Request, res: Response) => {
  try {
    await execute(
      "UPDATE gov_initiative_registry SET status = 'cancelled', updated_at = NOW() WHERE id = $1",
      [req.params.id]
    );
    res.json({ status: 'cancelled' });
  } catch (err: any) {
    res.status(400).json({ error: err.message });
  }
});

export default router;