Files
Plushealthtech/task/02-contact-page.md
Clive e5d1446791
Gitea CI/CD / build (push) Successful in 1m40s
Site pages and AI tasks.
2026-08-20 14:58:52 +10:00

3.3 KiB

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

  • GET /Contact renders with all five fields; build green.
  • Empty required fields / invalid email → validation messages, no mail sent.
  • 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).
  • 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.