This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user