← back to Norma

app/api/grants/[id]/proposals/route.ts

96 lines

import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
import { auditLog } from '@/lib/audit';
import { getOrgId } from '@/lib/orgId';

/**
 * GET /api/grants/[id]/proposals
 * List all proposals for a grant.
 */
export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> },
) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;
  const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;

  const { id: grantId } = await params;

  try {
    // Verify the grant belongs to this org
    if (orgId) {
      const grantCheck = await query(
        `SELECT id FROM grants WHERE id = $1 AND org_id = $2`,
        [grantId, orgId],
      );
      if (grantCheck.rowCount === 0) {
        return NextResponse.json({ error: 'Grant not found' }, { status: 404 });
      }
    }

    const result = await query(
      `SELECT * FROM grant_proposals WHERE grant_id = $1 ORDER BY created_at DESC`,
      [grantId],
    );
    return NextResponse.json({ proposals: result.rows });
  } catch (err) {
    console.error('[api/grants/proposals] GET error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to fetch proposals' }, { status: 500 });
  }
}

/**
 * POST /api/grants/[id]/proposals
 * Create a new proposal for a grant.
 */
export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> },
) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;
  const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;

  const { id: grantId } = await params;

  try {
    // Verify the grant belongs to this org
    if (orgId) {
      const grantCheck = await query(
        `SELECT id FROM grants WHERE id = $1 AND org_id = $2`,
        [grantId, orgId],
      );
      if (grantCheck.rowCount === 0) {
        return NextResponse.json({ error: 'Grant not found' }, { status: 404 });
      }
    }

    const body = await request.json();

    const result = await query(
      `INSERT INTO grant_proposals (grant_id, subject, recipient_email, recipient_name, body_html, body_text, created_by)
       VALUES ($1, $2, $3, $4, $5, $6, $7)
       RETURNING *`,
      [
        grantId,
        body.subject || 'Grant Proposal',
        body.recipient_email || null,
        body.recipient_name || null,
        body.body_html || '',
        body.body_text || null, auth.username,
      ],
    );

    const proposal = result.rows[0];
    const ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || undefined;
    await auditLog('proposal.created', 'grant_proposal', proposal.id, { grant_id: grantId }, ip);

    return NextResponse.json({ proposal }, { status: 201 });
  } catch (err) {
    console.error('[api/grants/proposals] POST error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to create proposal' }, { status: 500 });
  }
}