# Task 02 — Contact page with MailPit form - **Status:** Done — 2026-08-20 - **Depends on:** 01 - **Size:** M ## Goal Create the **Contact** page at `/Contact` with a form that emails its submission to MailPit (`tools.host.domain:1025`, recipient `alerts@plushealthtech.local`) via the existing `MailSender`. ## Files - Create: `plushealthtech/Pages/Contact.cshtml` - Create: `plushealthtech/Pages/Contact.cshtml.cs` ## Steps 1. **PageModel** (`ContactModel : PageModel`) - Inject `MailSender` (+ `ILogger`). - `[BindProperty]` fields with DataAnnotations: - `Name` — `[Required]`, `[StringLength(200)]` - `Email` — `[Required]`, `[EmailAddress]`, `[StringLength(320)]` - `Organization` — `[StringLength(200)]` (optional) - `Phone` — `[StringLength(50)]` (optional) - `Message` — `[Required]`, `[StringLength(5000)]` - `public string? Success { get; set; }` and `public string? Error { get; set; }` (simple re-render flags). - `OnGet()` — reset flags. - `OnPost()`: - If `!ModelState.IsValid` → return `Page()` (validation messages re-render). - Else build a plain-text body: one `Field: value` line per non-empty field, blank line, then the message. - `await _mailSender.SendAsync($"New contact form message — {Model.Name}", body);` - On success: `Success = "Thanks — your message has been sent. We'll reply to {Email}."` (re-render page with the panel). - Catch `SmtpException` (or general `Exception`) → log the exception, set `Error` to a generic "Your message could not be sent right now. Please try again or email us directly." — never surface SMTP details to the user. 2. **View** (`Contact.cshtml`) - `@page`, `@model ContactModel`, `ViewData["Title"] = "Contact"`. - Layout consistent with the Index page: light hero (heading + one-line intro), then a single form card (`bg-white p-8 rounded-2xl border border-slate-100 shadow-sm`) with the fields, Tailwind classes matching the site's existing inputs (`rounded-xl`, `border-slate-200`, `focus:border-brand` style), a submit button in brand color (`bg-[#1c8098] hover:bg-[#145f72] rounded-xl`). - `@Html.AntiforgeryToken()` in the form. - Validation messages per field (``); `Success` panel (green, replaces/above the form) and `Error` banner (red) rendered when set. - A short side note block: "We reply to the email address you provide — nothing else is stored." ## Acceptance criteria - [x] `GET /Contact` renders with all five fields; build green. - [x] Empty required fields / invalid email → validation messages, no mail sent. - [x] Valid submit → success panel shown; MailPit shows the message from `no-reply@plushealthtech.local` to `alerts@plushealthtech.local` with subject `New contact form message — {Name}` (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="/Contact"`, which auto-injects `__RequestVerificationToken` — the explicit `@Html.AntiforgeryToken()` from the spec is unnecessary and would duplicate the field. - SMTP-down path verified by running the app with `Smtp__Port=1` (env override, no config change): banner rendered, `SmtpException (SocketException 10061)` logged, response body clean.