Site pages and AI tasks.
Gitea CI/CD / build (push) Successful in 1m40s

This commit is contained in:
Clive
2026-08-20 14:58:52 +10:00
parent 152a998311
commit e5d1446791
51 changed files with 2876 additions and 30 deletions
+50
View File
@@ -0,0 +1,50 @@
# Task 01 — SMTP / MailPit configuration
- **Status:** Done — 2026-08-20
- **Depends on:** —
- **Size:** S
## Goal
Make the existing `Mail/` infrastructure (already in the tree and registered in `Program.cs`) actually usable: add the missing `"Smtp"` configuration section so `MailSender` has a host, port, sender, and recipient.
## Context
- `plushealthtech/Mail/MailSender.cs` — sends via `SmtpClient`, `EnableSsl = false`, no credentials. **Do not recreate or rewire** — it is registered as a singleton in `Program.cs` from the `"Smtp"` section.
- `plushealthtech/Mail/SmtpOptions.cs` — `Host` (default `localhost`), `Port` (default `1025`), `From` / `To` (default empty → `MailSender` throws if submitted while empty).
- **No `"Smtp"` section exists in any appsettings file** — that is the gap.
## Files
- Modify: `plushealthtech/appsettings.json`
## Steps
1. Add this section to `plushealthtech/appsettings.json` (base config — the MailPit server is the same in every environment):
```json
"Smtp": {
"Host": "tools.host.domain",
"Port": 1025,
"From": "[email protected]",
"To": "[email protected]"
}
```
`From` is an assumption (MailPit does not validate the sender) — single point of change if a different sender address is wanted.
2. `dotnet build plushealthtech.sln` — must stay green (no code changes expected).
3. Confirm `tools.host.domain` resolves from this machine (`nslookup tools.host.domain` or `ping -n 1 tools.host.domain`). If it only resolves on the internal network, note that dev machines will need a hosts entry.
## Acceptance criteria
- [x] `appsettings.json` contains the `Smtp` section exactly as above.
- [x] Build green.
- [x] Host name resolution confirmed (or documented as internal-network-only).
- [ ] End-to-end delivery proven in Task 13 (form submit → message visible in MailPit for `alerts@plushealthtech.local`).
## Notes
- `From` empty would throw `ArgumentException` in `MailMessage` — this task removes that failure mode.
- Resolution check (2026-08-20): `tools.host.domain` resolves to `10.10.0.30` via the internal router DNS (`vrouter.home.domain`, 10.10.0.1) and answers ping; TCP port 1025 is open. It is **internal-network-only** — dev machines off that network will need a hosts entry for `tools.host.domain`.
+52
View File
@@ -0,0 +1,52 @@
# 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.
+47
View File
@@ -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.
+35
View File
@@ -0,0 +1,35 @@
# Task 04 — Clinical Decision Support page
- **Status:** Done
- **Depends on:** —
- **Size:** M
## Goal
Complete the **Clinical Decision Support** page at `/ClinicalDecisionSupport`. The PageModel already exists in the tree — only the view is missing.
## Current state
- `plushealthtech/Pages/ClinicalDecisionSupport.cshtml.cs` — `ClinicalDecisionSupportModel` with an empty `OnGet()`. Keep it; no model changes needed (the view sets `ViewData["Title"]`).
- **`ClinicalDecisionSupport.cshtml` does not exist** → the route currently 404s.
## Files
- Create: `plushealthtech/Pages/ClinicalDecisionSupport.cshtml`
- (Optional) `plushealthtech/Pages/ClinicalDecisionSupport.cshtml.cs` — only if you want `OnGet` to set the title server-side.
## Steps
1. Create the view: `@page`, `@model ClinicalDecisionSupportModel`, `ViewData["Title"] = "Clinical Decision Support"`.
2. Sections (copy aligned with Index card #1 and the `#interoperability` section — reword, don't paste verbatim):
- **Hero** — "Evidence-based guidance at the point of care" + one-line subhead + CTA button → `/RequestDemo` ("Request a demo").
- **Feature cards** (3–4, `rounded-2xl border border-slate-100` cards): intelligent algorithms that reduce medical errors; optimized treatment choices; alerting & workflow integration; FHIR®/HL7v2 interoperability.
- **Interoperability band** (dark `bg-slate-900` like the Index section) — standards-based data exchange messaging.
- **CTA band** → `/RequestDemo`, secondary link → `/PatientManagement`.
3. Use existing Tailwind brand tokens (`text-brand`, `bg-brand-light`, `brand-accent`); no new CSS.
## Acceptance criteria
- [ ] `GET /ClinicalDecisionSupport` renders (no 404); build green.
- [ ] Page links to `/RequestDemo` (and optionally `/PatientManagement`) resolve.
- [ ] Styling consistent with Index page sections.
+29
View File
@@ -0,0 +1,29 @@
# Task 05 — Patient Management page
- **Status:** Done
- **Depends on:** —
- **Size:** M
## Goal
Create the **Patient Management** page at `/PatientManagement` — a content page in the same visual language as the Index page.
## Files
- Create: `plushealthtech/Pages/PatientManagement.cshtml`
- Create: `plushealthtech/Pages/PatientManagement.cshtml.cs`
## Steps
1. **PageModel** — standard `PageModel` with `OnGet()` (mirror `ClinicalDecisionSupportModel`); `ViewData["Title"]` set in the view.
2. **View** — `@page`, `@model PatientManagementModel`, `ViewData["Title"] = "Patient Management"`.
- **Hero** — "End-to-end patient workflow, from intake to discharge" + subhead (reword Index card #2) + CTA → `/RequestDemo`.
- **Feature cards** (3–4): intake & registration; scheduling & capacity; transparent patient communication channels; EHR integration (FHIR®-ready) — consistent `rounded-2xl border border-slate-100` cards.
- **CTA band** → `/RequestDemo`, secondary link → `/ClinicalDecisionSupport`.
3. Tailwind brand tokens only (`brand`, `brand-light`, `brand-accent`); no new CSS, no new JS.
## Acceptance criteria
- [x] `GET /PatientManagement` renders; build green.
- [x] CTAs link to existing/planned routes (`/RequestDemo`, `/ClinicalDecisionSupport`).
- [x] Styling consistent with Index and the CDS page.
+33
View File
@@ -0,0 +1,33 @@
# Task 06 — Veterinarian Solutions page
- **Status:** Complete
- **Depends on:** —
- **Size:** M
## Goal
Create the **Veterinarian Solutions** page at `/Veterinary`, using the veterinary sub-brand accent (`brand-vet` / `#0d9488`) per `Design.MD`.
## Files
- Create: `plushealthtech/Pages/Veterinary.cshtml`
- Create: `plushealthtech/Pages/Veterinary.cshtml.cs`
## Steps
1. **PageModel** — standard `PageModel` with `OnGet()`; title set in the view.
2. **View** — `@page`, `@model VeterinaryModel`, `ViewData["Title"] = "Veterinarian Solutions"`.
- **Hero** — "Purpose-built software for animal clinics" + subhead (reword Index card #3) + CTA → `/RequestDemo`.
- **Feature cards** (3–4): practice & client management; EHR records & imaging; species-tailored diagnostic decision tools; interoperability with existing hospital systems.
- **Accent rules** — use `brand-vet` (`#0d9488`) for the hero badge, card icon tiles (`bg-teal-50 text-brand-vet`, hover `bg-brand-vet text-white`) exactly like Index card #3, instead of the default `brand` teal.
- **CTA band** → `/RequestDemo`, secondary link → `/ClinicalDecisionSupport`.
## Acceptance criteria
- [x] `GET /Veterinary` renders; build green.
- [x] Page visibly uses the veterinary accent, distinct from the other product pages.
- [x] CTAs resolve.
## Notes
- The Index solution card carries `id="veterinary"` for the legacy nav anchor — leave that in place; the page route and the in-page anchor don't conflict.
+33
View File
@@ -0,0 +1,33 @@
# Task 07 — About Us page
- **Status:** Done (2026-08-20)
- **Depends on:** —
- **Size:** S/M
## Goal
Create the **About Us** page at `/About`.
## Files
- Create: `plushealthtech/Pages/About.cshtml`
- Create: `plushealthtech/Pages/About.cshtml.cs`
## Steps
1. **PageModel** — standard `PageModel` with `OnGet()`; title set in the view.
2. **View** — `@page`, `@model AboutModel`, `ViewData["Title"] = "About Us"`.
- **Hero** — who we are: Plus Health Tech builds interoperable clinical software; tone per `Design.MD` (trust, clinical precision, seamless interoperability, compassionate care).
- **What we do** — three product pillars as cards, each linking to its page:
- Clinical Decision Support → `/ClinicalDecisionSupport`
- Patient Management → `/PatientManagement`
- Veterinarian Solutions → `/Veterinary`
- **Values** (3–4 short items): interoperability by default, evidence-based, patient-first, built with clinicians.
- **CTA band** → `/RequestDemo`, secondary → `/Careers`.
3. Tailwind brand tokens only; no new CSS/JS.
## Acceptance criteria
- [x] `GET /About` renders; build green.
- [x] All three pillar cards link to their product pages; CTAs resolve. (Note: `/Careers` is a forward link — page lands with task 08.)
- [x] Copy matches the brand voice in `Design.MD` (reviewed against it before marking done).
+30
View File
@@ -0,0 +1,30 @@
# Task 08 — Careers page
- **Status:** Done
- **Depends on:** —
- **Size:** S
## Goal
Create the **Careers** page at `/Careers`. No job-board data or backend exists — this is a culture page with an "apply via the Contact page" path (assumption per plan §3.6).
## Files
- Create: `plushealthtech/Pages/Careers.cshtml`
- Create: `plushealthtech/Pages/Careers.cshtml.cs`
## Steps
1. **PageModel** — standard `PageModel` with `OnGet()`; title set in the view.
2. **View** — `@page`, `@model CareersModel`, `ViewData["Title"] = "Careers"`.
- **Hero** — "Build the future of interoperable clinical software."
- **Why join us** — 3–4 cards (mission-driven work, clinical+technical collaboration, modern .NET stack, flexibility — keep claims generic and reviewable).
- **How to apply** — explicit note that there is no public job board right now: "Tell us about yourself via the Contact page and we'll be in touch." CTA button → `/Contact`.
- Secondary CTA → `/About`.
3. Tailwind brand tokens only; no new CSS/JS.
## Acceptance criteria
- [x] `GET /Careers` renders; build green.
- [x] Apply CTA points at `/Contact`; no dead links.
- [x] No fabricated specific claims (salaries, exact team size) — keep it generic for legal/brand review.
+37
View File
@@ -0,0 +1,37 @@
# Task 09 — Terms & Conditions page
- **Status:** Done
- **Depends on:** —
- **Size:** S/M
## Goal
Create the **Terms & Conditions** page at `/Terms` with standard corporate boilerplate (flagged for legal review before production — plan §3.6).
## Files
- Create: `plushealthtech/Pages/Terms.cshtml`
- Create: `plushealthtech/Pages/Terms.cshtml.cs`
## Steps
1. **PageModel** — standard `PageModel` with `OnGet()`; title set in the view.
2. **View** — `@page`, `@model TermsModel`, `ViewData["Title"] = "Terms & Conditions"`.
- Last-updated date line (2026).
- Sections (h2 + short paragraphs, standard corporate wording):
1. Acceptance of terms
2. Services & product access (landing site, demo requests, contact forms)
3. **Clinical decision support disclaimer** — CDS output is decision *support*; not a substitute for professional clinical judgment, diagnosis, or treatment (important given the product line)
4. Intellectual property
5. User conduct / acceptable use
6. Limitation of liability
7. Governing law (placeholder jurisdiction — mark for legal)
8. Changes to these terms
9. Contact (→ `/Contact`)
3. Simple prose layout (max-w-3xl, readable line length); Tailwind only.
## Acceptance criteria
- [x] `GET /Terms` renders; build green. (verified: `dotnet build` 0 warnings/0 errors; live `GET /Terms` → HTTP 200)
- [x] Contains the CDS disclaimer section. (section 3 — highlighted; "decision support only", "not a substitute", "human or animal")
- [x] No live legal claims left unflagged — placeholders marked (e.g., jurisdiction) for review. (visible "finalized following legal review" callout + `[Jurisdiction to be confirmed]` pill + `<!-- legal -->` comments on §§6–7)
+32
View File
@@ -0,0 +1,32 @@
# Task 10 — Sign In page (placeholder)
- **Status:** Done
- **Depends on:** —
- **Size:** S
## Goal
Create the **Sign In** page at `/SignIn` as a placeholder. The repo has **no auth** (per CLAUDE.md) and no account backend — real sign-in is a separate project; this page is its future integration point (plan §3.4).
## Files
- Create: `plushealthtech/Pages/SignIn.cshtml`
- Create: `plushealthtech/Pages/SignIn.cshtml.cs`
## Steps
1. **PageModel** — standard `PageModel` with `OnGet()`; title set in the view.
2. **View** — `@page`, `@model SignInModel`, `ViewData["Title"] = "Sign In"`.
- Centered card (`bg-white p-8 rounded-2xl border border-slate-100 shadow-sm`, max-w-md):
- Icon (e.g. `fa-solid fa-right-to-bracket`) + heading "Sign in to your portal"
- Status line: "Portal sign-in is coming soon."
- Primary CTA → `/RequestDemo` ("Request a demo")
- Secondary CTA → `/Contact` ("Contact us instead")
- Do **not** add a fake username/password form — it would imply working auth.
3. Tailwind brand tokens only.
## Acceptance criteria
- [x] `GET /SignIn` renders; build green.
- [x] Both CTAs resolve.
- [x] No non-functional credentials form.
+39
View File
@@ -0,0 +1,39 @@
# Task 11 — Privacy Policy content refresh
- **Status:** Done — 2026-08-20
- **Depends on:** —
- **Size:** S
## Goal
Replace the template placeholder in the existing **Privacy** page (`/Privacy`, page already ships) with real policy content (flagged for legal review — plan §3.6).
## Files
- Modify: `plushealthtech/Pages/Privacy.cshtml`
- (No PageModel change needed — `PrivacyModel` already exists.)
## Current state
`plushealthtech/Pages/Privacy.cshtml` currently renders only:
> Use this page to detail your site's privacy policy.
## Steps
1. Keep `@page`, `@model PrivacyModel`, `ViewData["Title"] = "Privacy Policy"` and the route `/Privacy` (already linked nowhere yet — Task 12 will link it).
2. Replace the body with standard sections (h2 + paragraphs, max-w-3xl prose layout):
- **Information we collect** — the Contact and Request Demo form fields (name, email, organization, phone, practice type, products of interest, message).
- **How we use it** — to respond to inquiries and demo requests; messages are delivered to our team mailbox (`[email protected]`); no third-party sale/sharing.
- **Third-party services** — Font Awesome (cdnjs), analytics (analytics.rokoh.com), blog subdomain (blog.plushealthtech.com) — describe in one line each.
- **Cookies** — analytics cookies set by the above; no session cookies (no auth).
- **Data retention** — placeholder wording, marked for legal.
- **Your rights** — access/correction/deletion requests via the Contact page.
- **Contact** — link → `/Contact`.
- Last-updated date line (2026).
3. Tailwind only; no new CSS/JS.
## Acceptance criteria
- [x] `GET /Privacy` shows the new policy, not the template sentence.
- [x] Accurately reflects the two forms' fields and the actual third parties in the layout `<head>`.
- [x] Route unchanged: `/Privacy`.
+69
View File
@@ -0,0 +1,69 @@
# Task 12 — Navigation, footer & CTA link wiring
- **Status:** Done
- **Depends on:** 02, 03, 04, 05, 06, 07, 08, 09, 10, 11 (all routes must exist first)
- **Size:** M
## Goal
Point every navbar, footer, and Index CTA at the real pages; remove the `#contact` anchor pattern and the dead `#` links; make the (currently non-functional) mobile menu work.
## Files
- Modify: `plushealthtech/Pages/Shared/_Layout.cshtml`
- Modify: `plushealthtech/Pages/Index.cshtml`
- (Optional, if JS approach chosen) `plushealthtech/wwwroot/js/mobile-menu.js` or an inline `<script>` in the layout's `Scripts` section.
## Steps
### 1. Desktop nav (`_Layout.cshtml`, the `hidden md:flex` nav)
Replace the current anchor links with:
| Label | href |
|---|---|
| CDS | `/ClinicalDecisionSupport` |
| Patient Management | `/PatientManagement` |
| Veterinary | `/Veterinary` |
| About | `/About` |
| Careers | `/Careers` |
| Contact | `/Contact` |
| Blog | `https://blog.plushealthtech.com` (keep, external) |
(Removes the `#solutions` / `#interoperability` / `#veterinary` / `#about` anchors from the nav — those sections remain reachable by scrolling on the Index page.)
### 2. CTA buttons (`_Layout.cshtml`)
- `Sign In` → `/SignIn` (was `#contact`).
- `Request Demo` → `/RequestDemo` (was `#contact`).
### 3. Footer (`_Layout.cshtml`)
- **Remove `id="contact"` from the `<footer>`** — nothing may target it anymore.
- Solutions column → `/ClinicalDecisionSupport`, `/PatientManagement`, `/Veterinary` (labels: Clinical Decision Support / Patient Management / Veterinarian Solutions).
- Company column → `/About`, `/Careers`, `/Contact`.
- **New Legal column** → `/Privacy` (Privacy Policy), `/Terms` (Terms & Conditions). Grid becomes `md:grid-cols-5` (or 4 + legal under Company — keep it tidy at md width).
- Newsletter input block: leave as-is (decorative, no endpoint specified — plan §3.8).
### 4. Index page (`Index.cshtml`)
- Hero primary CTA "Explore Platform" (`#contact`) → `/RequestDemo`.
- Hero secondary CTA "View Solutions" — keep `#solutions` (valid in-page anchor).
- Solution cards "Learn more" / "Explore Vet Tech" links (`#`) → `/ClinicalDecisionSupport`, `/PatientManagement`, `/Veterinary` respectively.
- Keep `id="solutions"`, `id="interoperability"`, `id="veterinary"` anchors on the sections (still used by the hero CTA and any deep links).
### 5. Mobile menu (currently a no-op `<button>`)
Add a minimal toggle so the desktop nav is reachable below `md`:
- Simplest robust option: `<details>`/`<summary>` wrapper around the nav, or a 5–10 line vanilla JS toggle (`classList.toggle('hidden')`) in the layout's `Scripts` section.
- Close the menu when a link is tapped (optional nicety).
- No framework, no new dependency.
## Acceptance criteria
- [x] Build green.
- [x] Desktop nav has 7 working links; CTAs point at `/SignIn` and `/RequestDemo`.
- [x] Footer columns link to all pages incl. new Legal column; footer no longer carries `id="contact"`.
- [x] Index: zero `href="#"` left (grep to confirm); hero CTA lands on `/RequestDemo`.
- [x] Mobile menu opens/closes and all links are reachable at 375px width. (verified via rendered markup, CSS class presence, and JS review — not a live browser click-through)
- [x] No new utility classes required → `wwwroot/css/tailwind.css` rebuild **not** needed (verify; if any new class slips in, run `npm run build:css`). (`pb-6` was new — rebuilt with `npm run build:css`.)
+45
View File
@@ -0,0 +1,45 @@
# Task 13 — Verification & final checks
- **Status:** Not started
- **Depends on:** all of the above
- **Size:** S
## Goal
Prove the whole feature set works end-to-end before the change set is considered done.
## Checklist
### Build & run
- [ ] `dotnet build plushealthtech.sln` → 0 errors, 0 warnings.
- [ ] `dotnet run --project plushealthtech` → site up at https://localhost:7070.
### Link sweep (all 10 pages)
- [ ] Routes resolve: `/ClinicalDecisionSupport`, `/PatientManagement`, `/Veterinary`, `/About`, `/Careers`, `/Contact`, `/RequestDemo`, `/SignIn`, `/Terms`, `/Privacy`.
- [ ] Desktop nav: all 7 links + both CTAs land on the right page.
- [ ] Mobile (≤768px): menu toggles open; every nav link reachable.
- [ ] Footer: Solutions / Company / Legal columns all resolve.
- [ ] Index: hero CTAs + three solution-card links resolve.
- [ ] Grep for leftovers: `grep -rn "href=\"#\"" plushealthtech/Pages` → empty; `grep -rn '#contact' plushealthtech/Pages` → empty.
- [ ] `/Privacy` renders the new policy content, not the template sentence.
### Mail e2e (both forms)
- [ ] MailPit reachable at `tools.host.domain:1025` (from this machine — if not, note the hosts-entry requirement from Task 01).
- [ ] Contact form: valid submit → success panel; message appears in MailPit from `[email protected]` to `[email protected]`, subject `New contact form message — {Name}`, body contains all fields.
- [ ] Request Demo form: valid submit → success panel; subject `New demo request — {Organization}`; body contains selects' values.
- [ ] Validation: empty required fields / bad email → inline errors, **no** message in MailPit.
- [ ] Failure path: stop MailPit → submit → friendly error banner (no stack trace); app log shows the `SmtpException`; restart MailPit → submit succeeds again.
### Hygiene
- [ ] `dotnet build` still green with the layout changes.
- [ ] CSS bundle unchanged (no new utility classes) — if one slipped in, `npm run build:css` and re-verify.
- [ ] No changes needed to `Dockerfile` or the Gitea workflow (image pipeline already compiles CSS and ships the whole `wwwroot` + app) — confirm by re-reading the diff if in doubt.
- [ ] Working tree reviewed: only the files listed in Tasks 01–12 changed.
## Definition of done
All boxes ticked, build green, both forms demonstrably delivering into MailPit, and every one of the 10 pages reachable from the navbar or footer.