feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
residentialClientConfirmation,
|
|
|
|
|
residentialSalesAlert,
|
|
|
|
|
} from '@/lib/email/templates/residential-inquiry';
|
|
|
|
|
|
|
|
|
|
describe('residentialClientConfirmation', () => {
|
2026-06-25 22:07:47 +02:00
|
|
|
it('mirrors the website copy + reflects the chosen residence types', async () => {
|
|
|
|
|
const { subject, html, text } = await residentialClientConfirmation({
|
feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
firstName: 'Mia',
|
|
|
|
|
contactEmail: 'sales@portnimara.com',
|
|
|
|
|
residenceTypes: ['Two Bedroom Marina Villa', 'Five Bedroom Oceanfront Villa'],
|
|
|
|
|
portName: 'Port Nimara',
|
|
|
|
|
});
|
2026-06-25 22:07:47 +02:00
|
|
|
expect(subject).toBe('Port Nimara — Thank You for Your Interest');
|
|
|
|
|
expect(text).toContain(
|
|
|
|
|
'Thank you for expressing interest in the Two Bedroom Marina Villa and the Five Bedroom Oceanfront Villa',
|
|
|
|
|
);
|
|
|
|
|
expect(text).toContain('Our team has registered your interest');
|
|
|
|
|
expect(text).toContain('The Port Nimara Residences Team');
|
|
|
|
|
// Never leak the CRM brand name to a client.
|
|
|
|
|
expect(html).not.toContain('Port Nimara CRM');
|
feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
});
|
|
|
|
|
|
2026-06-25 22:07:47 +02:00
|
|
|
it('falls back to the website generic phrase when no types are selected', async () => {
|
feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
const { html } = await residentialClientConfirmation({
|
|
|
|
|
firstName: 'Sam',
|
|
|
|
|
contactEmail: 'sales@portnimara.com',
|
|
|
|
|
portName: 'Port Nimara',
|
|
|
|
|
});
|
2026-06-25 22:07:47 +02:00
|
|
|
expect(html).toContain('a Port Nimara Residence');
|
|
|
|
|
expect(html).not.toContain('Port Nimara CRM');
|
feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('residentialSalesAlert', () => {
|
2026-06-25 22:07:47 +02:00
|
|
|
it('renders residence type(s) + preferred contact + comments, with NO CRM mention', async () => {
|
feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
const { html, text } = await residentialSalesAlert({
|
|
|
|
|
fullName: 'Mia Ng',
|
|
|
|
|
email: 'mia@example.com',
|
|
|
|
|
phone: '+15551234',
|
|
|
|
|
residenceTypes: ['Two Bedroom Marina Villa'],
|
|
|
|
|
preferredContactMethod: 'phone',
|
|
|
|
|
notes: 'Looking for a winter completion.',
|
|
|
|
|
portName: 'Port Nimara',
|
|
|
|
|
});
|
|
|
|
|
expect(html).toContain('A new residential enquiry has come in');
|
|
|
|
|
expect(html).toContain('Residence type(s):');
|
|
|
|
|
expect(html).toContain('Two Bedroom Marina Villa');
|
|
|
|
|
expect(html).toContain('Preferred contact:');
|
|
|
|
|
expect(html).toContain('Phone call back');
|
|
|
|
|
expect(html).toContain('Looking for a winter completion.');
|
2026-06-25 22:07:47 +02:00
|
|
|
expect(text).toContain('- Port Nimara Residences');
|
|
|
|
|
// Residential internal alerts must not mention the CRM (recipient is external).
|
|
|
|
|
expect(html).not.toContain('CRM');
|
|
|
|
|
expect(html).not.toContain('to follow up');
|
feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
expect(text).toContain('Residence type(s): Two Bedroom Marina Villa');
|
|
|
|
|
expect(text).toContain('Preferred contact: Phone call back');
|
|
|
|
|
expect(text).toContain('Comments: Looking for a winter completion.');
|
2026-06-25 22:07:47 +02:00
|
|
|
expect(text).not.toContain('CRM');
|
feat(intake): residence-type capture + CRM-owned inquiry emails for website cutover
Website register-interest form now offers the 3 residence types as a
multi-select; the choice + preferred-contact flow into the CRM inquiry
payload, the inbox detail, and the residential emails.
- inquiry inbox detail surfaces residence type(s), preferred contact,
type-of-interest, comments (full data capture)
- residential-inquiry emails: client confirmation names the chosen
villa(s); sales alert converted to the canonical detail-line format
(uniform with berth/contact) + residence type(s)/preferred contact +
plain-text part
- website-intake-fields parses residence_types[] + method_of_contact
- contact_form alerts split to their own recipient key
(contact_notification_recipients)
- Residential Interests section: new residence_type field (schema +
migration 0099, validators, inline select on the detail)
- contact-form-alert email refactor shipped (interest-alert style)
Tests: website-intake-fields, residential-inquiry templates,
contact-form-alert, residential-interest validators.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
2026-06-25 20:58:53 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('omits optional rows cleanly when absent', async () => {
|
|
|
|
|
const { html } = await residentialSalesAlert({
|
|
|
|
|
fullName: 'Bob Smith',
|
|
|
|
|
email: 'bob@example.com',
|
|
|
|
|
phone: '+1999',
|
|
|
|
|
portName: 'Port Nimara',
|
|
|
|
|
});
|
|
|
|
|
expect(html).not.toContain('Residence type(s):');
|
|
|
|
|
expect(html).not.toContain('Preferred contact:');
|
|
|
|
|
expect(html).toContain('Bob Smith');
|
|
|
|
|
});
|
|
|
|
|
});
|