53 lines
3.3 KiB
Markdown
53 lines
3.3 KiB
Markdown
# 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 `[email protected]`) via the existing `MailSender`.
|
|
|
|
## Files
|
|
|
|
- Create: `plushealthtech/Pages/Contact.cshtml`
|
|
- Create: `plushealthtech/Pages/Contact.cshtml.cs`
|
|
|
|
## Steps
|
|
|
|
1. **PageModel** (`ContactModel : PageModel`)
|
|
- Inject `MailSender` (+ `ILogger<ContactModel>`).
|
|
- `[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 (`<span asp-validation-for="...">`); `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 `[email protected]` to `[email protected]` 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.
|