# Inquiry Notifications System Design

Migrates the ActivePieces-powered inquiry notification flow into the CRM. When a client registers interest via the Port Nimara website, the system sends a confirmation email to the client and notifies the sales team -- all using the CRM's own database and email infrastructure instead of NocoDB + ActivePieces.

## Scope

- Expand the public interest API to accept all website form fields
- Add client address storage (multi-address with primary flag)
- Send branded confirmation email to the client
- Send notification to sales team (CRM users + optional external recipients)
- Make notification recipients and contact email configurable by admins

## Database Changes

### New table: `client_addresses`

| Column           | Type              | Notes                                                            |
| ---------------- | ----------------- | ---------------------------------------------------------------- |
| `id`             | uuid PK           | `crypto.randomUUID()`                                            |
| `client_id`      | uuid FK → clients | cascade delete                                                   |
| `port_id`        | uuid FK → ports   | cascade delete                                                   |
| `label`          | text              | e.g., "Home", "Office", "Billing"                                |
| `street_address` | text              |                                                                  |
| `city`           | text              |                                                                  |
| `state_province` | text              |                                                                  |
| `postal_code`    | text              |                                                                  |
| `country`        | text              |                                                                  |
| `is_primary`     | boolean           | default `true`, one-primary-per-client enforced in service layer |
| `created_at`     | timestamp         | default `now()`                                                  |
| `updated_at`     | timestamp         | default `now()`                                                  |

Schema file: `src/lib/db/schema/clients.ts` (alongside existing client tables).
Relations: added to `src/lib/db/schema/relations.ts` (client has many addresses).

### No changes to existing tables

- `clients.preferred_contact_method` already exists -- we populate it from the form.
- `interests.berth_id` already exists -- we resolve `mooringNumber` to a berth and link it.
- `notifications.type` already has `new_registration` -- we fire it.

## Public API Changes

### `POST /api/public/interests`

Expanded request schema:

```typescript
// Required
firstName: string; // max 100
lastName: string; // max 100
email: string; // email format
phone: string;

// Optional
preferredContactMethod: 'email' | 'phone' | 'sms';
mooringNumber: string; // e.g., "A3" -- resolved against berths.mooring_number
companyName: string;
yachtName: string;
yachtLengthFt: number;
yachtWidthFt: number;
yachtDraftFt: number;
preferredBerthSize: string;
notes: string; // max 2000
address: {
  street: string;
  city: string;
  stateProvince: string;
  postalCode: string;
  country: string;
}

// Backward compatibility
fullName: string; // accepted if firstName/lastName not provided
```

Backward compatibility: if `fullName` is provided without `firstName`/`lastName`, it is used as-is for `clients.full_name`. If `firstName`+`lastName` are provided, they are concatenated.

### Behavior after record creation

1. Resolve `mooringNumber` against `berths.mooring_number` for the port. Link `interests.berth_id` if found; leave null if not.
2. Store `address` in `client_addresses` with `is_primary: true` and `label: 'Primary'`.
3. Set `clients.preferred_contact_method` from the form value.
4. Queue client confirmation email (see Email Templates below).
5. Fire `new_registration` notifications to sales team (see Notification Flow below).
6. Return `201 { data: { id, message } }` unchanged.

Rate limiting remains 5 requests/hour per IP.

## Email Templates

Located in `src/lib/email/templates/`. Each exports a function that accepts a typed data object and returns `{ subject: string, html: string, text: string }`.

### `inquiry-client-confirmation.ts`

Sent to the client who submitted the form.

**Input data:**

- `firstName` -- for the greeting
- `mooringNumber` -- berth identifier (nullable)
- `contactEmail` -- from `inquiry_contact_email` system setting

**Subject:** "Thank You for Your Interest in Berth {mooringNumber}" or "Thank You for Your Interest in a Port Nimara Berth" if no berth.

**Body:** Greeting with first name, confirmation their interest is registered, mention they'll be contacted by preferred method, link to the contact email address.

**Styling:** Branded -- Port Nimara logo, background image, white card layout. Matches the existing ActivePieces client confirmation template.

### `inquiry-sales-notification.ts`

Sent to CRM users and optional external recipients.

**Input data:**

- `fullName`
- `email`
- `phone`
- `mooringNumber` (nullable, defaults to "None")
- `crmUrl` -- link to the interest detail page in the CRM (built from port slug + interest ID)

**Subject:** "New Interest - Port Nimara"

**Body:** Notifies that a new interest has been registered, shows client details and berth selected, links to the CRM.

**Styling:** Branded -- Port Nimara logo, background image, white card layout. Matches the existing ActivePieces admin notification template.

Both templates include a plain-text fallback.

## Notification & Delivery Flow

### Client confirmation email

1. After record creation, queue a `send-inquiry-confirmation` job on the `email` BullMQ queue.
2. Email worker renders the `inquiry-client-confirmation` template with the interest data.
3. Sends via system SMTP (`src/lib/email/index.ts`).
4. No in-app notification (client is not a CRM user).

### Sales team notification

1. Query all users on the port who have `interests` read permission via their role.
2. For each user, call `createNotification()` with type `new_registration`.
   - The existing notification service checks `user_notification_preferences` (in-app / email / both / neither).
   - Creates in-app notification + Socket.IO push if `in_app: true`.
   - Queues `send-notification-email` job if `email: true`.
3. Fetch `inquiry_notification_recipients` system setting for the port.
4. For each external email, queue a `send-inquiry-sales-notification` job on the `email` queue (bypasses notification preferences since these are not CRM users).

### Independence

Client confirmation and sales notifications are independent -- a failure in one does not block the other. The `201` response returns immediately after record creation, before any emails are sent.

## Admin Configuration

Two new system settings, managed via the existing admin settings UI:

### `inquiry_contact_email` (string, per-port)

The reply-to / contact email shown in client confirmation emails.

- Default: `sales@portnimara.com`
- Displayed as a mailto link in the client confirmation email.

### `inquiry_notification_recipients` (JSON array of strings, per-port)

Additional external email addresses that receive the sales team notification.

- Default: `[]` (empty)
- Only CRM users with interests permissions are notified by default.
- External recipients receive the sales notification email directly.

### Existing infrastructure (no changes needed)

- **Which CRM users get notified**: controlled by roles/permissions.
- **How each user receives notifications**: `user_notification_preferences` table.
- **Admin settings UI**: already supports custom key-value pairs in `system_settings`.

## Files to Create or Modify

### New files

- `src/lib/db/schema/client-addresses.ts` -- (or added to `clients.ts`)
- `src/lib/email/templates/inquiry-client-confirmation.ts`
- `src/lib/email/templates/inquiry-sales-notification.ts`

### Modified files

- `src/lib/db/schema/clients.ts` -- add `clientAddresses` table export
- `src/lib/db/schema/index.ts` -- re-export new table
- `src/lib/db/schema/relations.ts` -- add client addresses relations
- `src/lib/validators/public-interest.ts` (or wherever `publicInterestSchema` lives) -- expand schema
- `src/app/api/public/interests/route.ts` -- berth resolution, address storage, notification + email triggers
- `src/lib/queue/workers/email.ts` -- handle `send-inquiry-confirmation` and `send-inquiry-sales-notification` jobs
- `src/lib/services/interests.service.ts` -- helper to find users with interests permissions on a port
- `src/app/(dashboard)/[portSlug]/admin/settings/settings-manager.tsx` -- register the two new setting keys

## Out of Scope

- Editing email templates from the admin UI (templates are in code).
- Supplemental forms for collecting missing info (separate feature using existing `form_templates` / `form_submissions` infrastructure).
- Documenso EOI integration with address merge fields (separate feature).
- Changes to the Port Nimara website form itself (website team wires the form to our API).
