# Task 03 — Request Demo page with MailPit form - **Status:** Done — 2026-08-20 - **Depends on:** 01 - **Size:** M ## Goal Create the **Request Demo** page at `/RequestDemo` with a form that emails its submission to MailPit (`tools.host.domain:1025`, recipient `alerts@plushealthtech.local`) via the existing `MailSender`. Same send/success/error pattern as the Contact page (Task 02). ## Files - Create: `plushealthtech/Pages/RequestDemo.cshtml` - Create: `plushealthtech/Pages/RequestDemo.cshtml.cs` ## Steps 1. **PageModel** (`RequestDemoModel : PageModel`) - Inject `MailSender` (+ logger). - `[BindProperty]` fields with DataAnnotations: - `Name` — `[Required]`, `[StringLength(200)]` - `Email` — `[Required]`, `[EmailAddress]`, `[StringLength(320)]` - `Organization` — `[Required]`, `[StringLength(200)]` - `PracticeType` — select: Healthcare system, Hospital, Clinic, Veterinary practice, Other (optional) - `ProductsOfInterest` — select: Clinical Decision Support, Patient Management, Veterinary Solutions, Multiple / not sure (optional) - `Message` — `[StringLength(5000)]` (optional) - `Success` / `Error` flags, `OnGet` reset. - `OnPost()` same contract as Task 02: validate → build plain-text `Field: value` body → `SendAsync($"New demo request — {Organization}", body)` → success flag; SMTP failure → generic error banner + log. 2. **View** (`RequestDemo.cshtml`) - `@page`, `@model RequestDemoModel`, `ViewData["Title"] = "Request Demo"`. - Marketing-flavored hero (one-line value prop, 2–3 trust bullets like "FHIR®-ready" / "24/7 clinical support" — reuse Index hero metrics) + the form card, Tailwind styling consistent with the site. - `@Html.AntiforgeryToken()`, per-field validation spans, success/error rendering identical in behavior to Contact. - Close with a secondary CTA: "Prefer to talk? Visit the Contact page." → `/Contact`. ## Acceptance criteria - [x] `GET /RequestDemo` renders; build green. - [x] Required fields enforced; selects render all options. - [x] Valid submit → success panel shown; MailPit shows the message from `no-reply@plushealthtech.local` to `alerts@plushealthtech.local` with subject `New demo request — {Organization}` (e2e verified 2026-08-20, ahead of Task 13). - [x] With MailPit down → friendly error banner, exception in app log, no stack trace in the browser. ## Notes - Antiforgery: the form uses `asp-page="/RequestDemo"`, which auto-injects `__RequestVerificationToken` — the explicit `@Html.AntiforgeryToken()` from the spec is unnecessary and would duplicate the field (same approach as the Contact page). - SMTP-down path verified by running the app with `Smtp__Host=127.0.0.1 Smtp__Port=1` (env override, no config change): banner rendered, `SmtpException (SocketException 10061)` logged with sender context, response body clean. - Validation confirmed over HTTP: empty POST → "required" messages on Name/Email/Organization only; bad email → "not a valid e-mail address"; both selects render their full option lists with a placeholder.