This commit is contained in:
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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`.)
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user