← back to Norma

app/api/email-sends/route.ts

145 lines

/**
 * GET /api/email-sends — List email campaigns, optionally filtered by type/status
 * POST /api/email-sends — Create a new email campaign record
 */

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

export async function GET(request: NextRequest) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;

  const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
  const url = new URL(request.url);
  const emailType = url.searchParams.get('type');
  const status = url.searchParams.get('status');
  const limit = Math.min(parseInt(url.searchParams.get('limit') || '50', 10), 200);

  try {
    const conditions: string[] = [];
    const params: unknown[] = [];
    let paramIdx = 1;

    if (orgId) {
      conditions.push(`org_id = $${paramIdx++}`);
      params.push(orgId);
    }

    if (emailType) {
      conditions.push(`email_type = $${paramIdx++}`);
      params.push(emailType);
    }

    if (status) {
      conditions.push(`status = $${paramIdx++}`);
      params.push(status);
    }

    const where = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : '';

    // Get campaigns
    const campaignsRes = await query(
      `SELECT id, email_type, subject, body_html, body_text, target_audience,
              status, tone, sent_at, sent_count, open_count, click_count,
              ai_generated, target_name, target_email, created_at, updated_at
       FROM email_campaigns
       ${where}
       ORDER BY created_at DESC
       LIMIT $${paramIdx}`,
      [...params, limit],
    );

    // Get counts by type
    const typeConditions: string[] = [];
    const typeParams: unknown[] = [];
    let typeIdx = 1;
    if (orgId) {
      typeConditions.push(`org_id = $${typeIdx++}`);
      typeParams.push(orgId);
    }
    const typeWhere = typeConditions.length > 0 ? 'WHERE ' + typeConditions.join(' AND ') : '';

    const countsRes = await query(
      `SELECT email_type, COUNT(*)::int as count
       FROM email_campaigns
       ${typeWhere}
       GROUP BY email_type`,
      typeParams,
    );

    const typeCounts: Record<string, number> = {};
    for (const row of countsRes.rows) {
      typeCounts[row.email_type || 'general'] = row.count;
    }

    return NextResponse.json({
      campaigns: campaignsRes.rows,
      typeCounts,
      total: campaignsRes.rows.length,
    });
  } catch (err) {
    console.error('[api/email-sends] GET error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to load email campaigns' }, { status: 500 });
  }
}

export async function POST(request: NextRequest) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;

  const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;

  try {
    const body = await request.json();
    const {
      email_type = 'general',
      subject,
      body_html,
      body_text,
      target_audience = [],
      status = 'draft',
      tone = 'formal',
      target_name,
      target_email,
    } = body;

    if (!subject || typeof subject !== 'string' || !subject.trim()) {
      return NextResponse.json({ error: 'subject is required' }, { status: 400 });
    }

    if (!body_html || typeof body_html !== 'string') {
      return NextResponse.json({ error: 'body_html is required' }, { status: 400 });
    }

    const res = await query(
      `INSERT INTO email_campaigns
        (email_type, subject, body_html, body_text, target_audience, status, tone,
         target_name, target_email, org_id, ai_generated, sent_at)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
       RETURNING *`,
      [
        email_type,
        subject.trim(),
        body_html,
        body_text || '',
        target_audience,
        status,
        tone,
        target_name || null,
        target_email || null,
        orgId || null,
        true,
        status === 'sent' ? new Date().toISOString() : null,
      ],
    );

    return NextResponse.json({ campaign: res.rows[0] }, { status: 201 });
  } catch (err) {
    console.error('[api/email-sends] POST error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to create email campaign' }, { status: 500 });
  }
}