48 lines
3.0 KiB
Markdown
48 lines
3.0 KiB
Markdown
# 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 `[email protected]`) 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 `[email protected]` to `[email protected]` 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.
|