feat(intake): CRM-owned website inquiry emails + in-app notifications
Flag-gated (website_intake_email_enabled, default OFF) sending of registrant confirmation + staff alert for inquiries captured at /api/public/website-inquiries, reusing the branded berth + residential templates and adding contact-form client-confirmation + sales-alert templates. In-app (bell) notifications fire on every fresh capture, independent of the flag. Recipients resolve from the existing inquiry_/residential_notification_recipients settings; fires only on a fresh (non-deduped) insert so retries never re-send. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,11 @@ import {
|
||||
} from '@/lib/errors';
|
||||
import { logger } from '@/lib/logger';
|
||||
import { checkRateLimit, rateLimiters } from '@/lib/rate-limit';
|
||||
import {
|
||||
isWebsiteIntakeEmailEnabled,
|
||||
notifyWebsiteSubmissionInApp,
|
||||
sendWebsiteSubmissionEmails,
|
||||
} from '@/lib/services/website-intake-email.service';
|
||||
|
||||
/**
|
||||
* POST /api/public/website-inquiries
|
||||
@@ -169,6 +174,40 @@ export async function POST(req: NextRequest) {
|
||||
},
|
||||
'website inquiry captured',
|
||||
);
|
||||
|
||||
// In-app (bell) notifications for reps - always on a fresh capture,
|
||||
// independent of email ownership, so inquiries surface in the CRM inbox.
|
||||
void notifyWebsiteSubmissionInApp({
|
||||
portId: port.id,
|
||||
portSlug: parsed.port_slug,
|
||||
kind: parsed.kind,
|
||||
submissionId: parsed.submission_id,
|
||||
payload: parsed.payload,
|
||||
}).catch((err) =>
|
||||
logger.error(
|
||||
{ err, submissionId: parsed.submission_id },
|
||||
'Failed to create website-intake notifications',
|
||||
),
|
||||
);
|
||||
|
||||
// Flag-gated CRM-owned emails (registrant confirmation + staff alert).
|
||||
// Fire only on this fresh-insert branch so a redelivery never re-sends.
|
||||
// Inline fire-and-forget: a send failure must not 500 the capture POST.
|
||||
if (await isWebsiteIntakeEmailEnabled(port.id)) {
|
||||
void sendWebsiteSubmissionEmails({
|
||||
portId: port.id,
|
||||
portSlug: parsed.port_slug,
|
||||
kind: parsed.kind,
|
||||
submissionId: parsed.submission_id,
|
||||
payload: parsed.payload,
|
||||
}).catch((err) =>
|
||||
logger.error(
|
||||
{ err, submissionId: parsed.submission_id },
|
||||
'Failed to send website-intake emails',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// L34 carve-out: deliberate bespoke `{ id, deduped }` shape (NOT the
|
||||
// `{ data }` envelope). This is the public website's intake contract —
|
||||
// the external marketing site reads `id`/`deduped` off the JSON root.
|
||||
|
||||
Reference in New Issue
Block a user