# IAP — Leadership Review Notification
**Date:** 2026-03-23
**Status:** Approved

## Overview

When an HR editor sets an application status to "Leadership Review" in `application_detail.php`, an inline notification panel expands below the status form. The editor selects which executives to notify (pre-checked by default), optionally adds a custom message, and submits. At least one recipient is required. Selected executives receive a brief SendGrid email with applicant details and a link to the application.

## Database

### New Table: `iap_leadership_recipients`

```sql
CREATE TABLE iap_leadership_recipients (
    id         INT AUTO_INCREMENT PRIMARY KEY,
    name       VARCHAR(100)  NOT NULL,
    email      VARCHAR(255)  NOT NULL,
    active     TINYINT(1)    NOT NULL DEFAULT 1,
    sort_order INT           NOT NULL DEFAULT 0
);

INSERT INTO iap_leadership_recipients (name, email, sort_order) VALUES
    ('Cassie Myers',    'cmyers@whitewatercw.com', 1),
    ('Carmen Trujillo', 'carmen@whitewatercw.com', 2);
```

- `active = 0` hides a recipient without deleting them
- New execs are added via INSERT — no code changes required

## Files Changed

### `app/HR/IAP/application_detail.php`

**PHP (top of file, after DB connect):**
- Query `iap_leadership_recipients WHERE active = 1 ORDER BY sort_order` and store results in `$leadership_recipients`

**HTML (Status Management section):**
- Add a hidden `<div id="leadership-notify">` immediately after the closing `</div>` of the `.status-form-row` div (not inside it — the flex row contains the select and notes textarea side-by-side; the panel goes below that entire row)
- Contents:
  - Heading: "Notify Leadership"
  - Checkbox list — one per recipient from `$leadership_recipients`, all pre-checked, `name="notify_recipients[]"` value = email address
  - Optional custom message: `<textarea name="notify_message">` with placeholder "Add a note for the leadership team..."
  - Inline error `<span id="notify-error">` for validation feedback

**JS (existing `<script>` block):**
- On status `<select>` change: show `#leadership-notify` when value is `"Leadership Review"`, hide otherwise
- On `#statusForm` submit: if `"Leadership Review"` is selected and zero checkboxes are checked, show error "At least one recipient is required" and prevent submission
- Pass `notify_recipients[]` and `notify_message` through the existing `FormData(this)` — no extra wiring needed since they're named inputs inside the form

### `app/HR/IAP/sync_application_status.php`

**Accept new POST params:**
- `$notify_recipients = $_POST['notify_recipients'] ?? []` — array of email addresses
- `$notify_message = trim($_POST['notify_message'] ?? '')`

**After successful status UPDATE, when `$new_status === 'Leadership Review'`:**
- Query `internal_applications` for `first_name`, `last_name`, `position_applying_for`, `desired_location` by `application_id` (before `$conn->close()`)
- **Server-side recipient validation:** query `SELECT email FROM iap_leadership_recipients WHERE active = 1` and intersect with `$notify_recipients` — discard any submitted email not in the active recipients list. This prevents arbitrary email injection via forged POST.
- Build email body — wrap every dynamic value in `htmlspecialchars($val, ENT_QUOTES | ENT_HTML5, 'UTF-8')`, including `first_name`, `last_name`, `position_applying_for`, `desired_location`, and `notify_message`:
  - **From:** `noreply@whitewatercw.com` / `Internal Applications`
  - **Subject:** `Leadership Review — {esc(first_name)} {esc(last_name)}`
  - **Body (HTML):**
    ```
    <h2>Leadership Review — {esc(first_name)} {esc(last_name)}</h2>
    <p><strong>Position Applied:</strong> {esc(position_applying_for)}</p>
    <p><strong>Desired Location:</strong> {esc(desired_location)}</p>
    [if notify_message not empty:]
    <p><strong>Note:</strong> {esc(notify_message)}</p>
    <p><a href="https://intranet.whitewatercw.com/hr/iap.php?page=detail&id={application_id}">View Application</a></p>
    ```
    (where `esc()` = `htmlspecialchars($val, ENT_QUOTES | ENT_HTML5, 'UTF-8')`)
- Loop validated recipients — instantiate a **new `\SendGrid\Mail\Mail()` object inside each loop iteration** (reusing one object would accumulate recipients across sends)
- If SendGrid returns HTTP >= 400: `error_log()` the failure, continue — do NOT block the status save

## Behavior Notes

- The notification panel is hidden on page load regardless of current status
- Panel only appears when the editor actively selects "Leadership Review" in the dropdown
- If editor changes away from "Leadership Review" before saving, panel hides and validation is skipped
- Status save is not blocked by SendGrid failure — DB update always goes through if valid
- At least 1 recipient required: validated client-side (JS) only — server side does not enforce (consistent with existing form validation pattern)
- If the validated recipient array is empty after server-side intersection (e.g. forged POST with invalid emails), skip the send block entirely — loop over an empty array, log nothing, proceed normally

## Out of Scope

- No admin UI for managing `iap_leadership_recipients` — add/remove via direct DB INSERT/UPDATE
- No notification for status changes other than "Leadership Review"
- No reply-to or CC on the leadership emails
- No record in `internal_application_audit` of which recipients were notified (audit log records the status change only)
