← back to Norma
app/api/onboard/[id]/route.ts
131 lines
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
import { getOrgId } from '@/lib/orgId';
// ─── GET /api/onboard/[id] ────────────────────────────────────────────────────
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const { id } = await params;
if (!id) {
return NextResponse.json({ error: 'Missing id' }, { status: 400 });
}
const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
try {
const orgFilter = orgId ? ' AND od.org_id = $2' : '';
const { rows } = await query<Record<string, unknown>>(
`SELECT
od.*,
o.name AS org_full_name,
o.mission,
o.website,
o.email,
o.phone,
o.founded_year,
o.employee_count,
o.ntee_code AS org_ntee_code,
o.state AS org_state,
o.city,
o.ein
FROM onboard_discoveries od
LEFT JOIN organizations o ON o.id = od.org_id
WHERE od.id = $1${orgFilter}
LIMIT 1`,
orgId ? [id, orgId] : [id],
);
if (rows.length === 0) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}
return NextResponse.json({ item: rows[0] });
} catch (err: unknown) {
console.error('[api/onboard/[id] GET] Error:', (err as Error).message);
return NextResponse.json({ error: (err as Error).message }, { status: 500 });
}
}
// ─── PUT /api/onboard/[id] — update status / notes ───────────────────────────
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const { id } = await params;
if (!id) {
return NextResponse.json({ error: 'Missing id' }, { status: 400 });
}
try {
const body = (await request.json()) as {
status?: string;
notes?: string;
contacted_at?: string;
};
const { status, notes, contacted_at } = body;
const VALID_STATUSES = ['discovered', 'contacted', 'onboarded', 'rejected'];
if (status && !VALID_STATUSES.includes(status)) {
return NextResponse.json(
{ error: `Invalid status. Must be one of: ${VALID_STATUSES.join(', ')}` },
{ status: 400 },
);
}
// Build dynamic SET clause
const setClauses: string[] = ['updated_at = NOW()'];
const queryParams: unknown[] = [];
if (status !== undefined) {
queryParams.push(status);
setClauses.push(`status = $${queryParams.length}`);
// Auto-set contacted_at when status changes to 'contacted'
if (status === 'contacted' && !contacted_at) {
setClauses.push(`contacted_at = NOW()`);
}
}
if (notes !== undefined) {
queryParams.push(notes);
setClauses.push(`notes = $${queryParams.length}`);
}
if (contacted_at !== undefined) {
queryParams.push(contacted_at);
setClauses.push(`contacted_at = $${queryParams.length}`);
}
queryParams.push(id);
const idIdx = queryParams.length;
const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
let orgFilter = '';
if (orgId) {
queryParams.push(orgId);
orgFilter = ` AND org_id = $${queryParams.length}`;
}
const { rowCount } = await query(
`UPDATE onboard_discoveries SET ${setClauses.join(', ')} WHERE id = $${idIdx}${orgFilter}`,
queryParams,
);
if ((rowCount ?? 0) === 0) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}
return NextResponse.json({ success: true, id, status, notes });
} catch (err: unknown) {
console.error('[api/onboard/[id] PUT] Error:', (err as Error).message);
return NextResponse.json({ error: (err as Error).message }, { status: 500 });
}
}