Skip to content

Add intake locking, patient list, API client, security hardening, and CI fix#59

Merged
BlackHouseDeveloper merged 4 commits intoBackend-Integration-Completionfrom
Patient-Intake-To-Dos-sprint
Mar 28, 2026
Merged

Add intake locking, patient list, API client, security hardening, and CI fix#59
BlackHouseDeveloper merged 4 commits intoBackend-Integration-Completionfrom
Patient-Intake-To-Dos-sprint

Conversation

@BlackHouseDeveloper
Copy link
Copy Markdown
Owner

@BlackHouseDeveloper BlackHouseDeveloper commented Mar 28, 2026

Pull Request

Summary

Introduce intake draft/locking support end-to-end (API + UI), clinician patient listing, and API token forwarding for ServerAPI calls. Includes security hardening, PHI reduction, accessibility improvements, correctness fixes applied from code review feedback, and a CI build fix for unresolved merge conflict markers.

Title

Add intake locking, clinician patient list, API access token forwarding, security hardening, and CI fix

Description

What Changed

  • Added server endpoints: GET /intake/patient/{id}/draft, PUT /intake/{id}, POST /intake/{id}/submit, POST /intake/{id}/lock
  • Added clinician patient listing endpoint and DTOs: UpdateIntakeRequest, PatientListItemResponse
  • Persisted/respected IsLocked/IsSubmitted state across services and the mock implementation
  • Introduced Blazor ClinicianPatientSelector component
  • Wired IntakeApiService to call the new endpoints and updated intake wizard UI/state (locked intakes, improved error handling)
  • Added PTDocClaimTypes.ApiAccessToken and an ApiAccessTokenForwardingHandler and registered it with the HttpClient factory
  • Minor plumbing to create/propagate the API access token claim during sign-in
  • PHI reduction: Removed DateOfBirth from PatientListItemResponse DTO and the ListPatients projection — the clinician selector only requires ID, display name, and MRN
  • GetDraftByPatient fix: Now filters to unlocked intakes only (WHERE !IsLocked) and orders by LastModifiedUtc DESC, ensuring the most recent editable draft is returned
  • UpdateIntake validation: Added explicit server-side TemplateVersion max-length (50) check, since Minimal API endpoints do not auto-apply DataAnnotations
  • SubmitIntake HIPAA enforcement: Parses the Consents JSON and requires hipaaAcknowledged: true before allowing submission — previously a client could submit with {"hipaaAcknowledged": false} and bypass this check
  • CreateTemporaryPatientAndDraftIntakeAsync fix: Throws ArgumentException when DateOfBirth is null instead of silently defaulting to 1970-01-01
  • SaveDraftAsync fix: Switched from an anonymous object to the typed UpdateIntakeRequest DTO for compile-time contract safety
  • SubmitAsync fix: Throws InvalidOperationException when the intake is not found or already locked, replacing the previous silent no-op that produced incorrect UI state
  • ToDraft fix: Maps IsLocked from the API response so locked-but-not-submitted intakes load as read-only in the wizard
  • ClinicianPatientSelector accessibility: Search placeholder corrected to "Search by name or MRN"; <select> element now has aria-label="Select patient" for screen reader support
  • ClinicianPatientSelector performance: Replaced eager 250-patient client-side load with server-side search using a 300ms debounced System.Timers.Timer (Stop/Reset pattern, proper exception handling, patient list preserved on transient errors)
  • ApiAccessTokenForwardingHandler fix: Falls back to HttpContext.GetTokenAsync("access_token") (OIDC-saved token via SaveTokens = true) when the PTDocClaimTypes.ApiAccessToken claim is absent, ensuring the Entra External ID flow also forwards the bearer token
  • CI fix: Resolved unresolved git merge conflict markers left in PfptRoleComplianceTests.cs (lines 196–200) that caused CS8300 build errors across all 12 CI check jobs
  • SubmitIntake visibility fix: Changed SubmitIntake handler from private to internal in IntakeEndpoints.cs so PfptRoleComplianceTests can invoke it directly via the existing [assembly: InternalsVisibleTo("PTDoc.Tests")]
  • Test fixture update: Updated UC2_IntakeForm_LocksAfterSubmit_ViaRealHandler to include Consents = "{\"hipaaAcknowledged\":true}" so the test satisfies the HIPAA consent validation added to the SubmitIntake handler

Why
Clinicians need a stable workflow for working with intake drafts while preventing edits to locked or submitted intakes, and the ServerAPI must receive the authenticated user's API token. Code review identified PHI over-exposure, a HIPAA consent bypass, silent failure modes in the UI service layer, an accessibility gap in the patient selector, and a missing OIDC token fallback — all of which have been corrected. A subsequent merge introduced unresolved conflict markers that broke every CI job; those have been resolved along with the test-accessibility and test-correctness issues they exposed.

How
New ASP.NET endpoints + DTOs, token forwarding via delegating handler with OIDC fallback, server-side HIPAA consent JSON parsing, reduced PHI projection in the list endpoint, server-side debounced search in the Blazor selector component (reusable timer, error-safe async lambda), typed DTO usage throughout the UI service layer, internal visibility on SubmitIntake for direct handler testing, and resolved merge conflict markers in the compliance test file.

References

  • (TODO)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Code refactoring

Impact Areas

  • User interface (PTDoc.UI shared components)
  • Core business logic (PTDoc.Core)
  • Application services (PTDoc.Application)
  • Infrastructure/data access (PTDoc.Infrastructure)
  • API endpoints (PTDoc.Api)
  • Database schema/migrations
  • Build system
  • CI/CD workflows

Checklist

Core Requirements

  • Architecture Compliance: Web is DB-stateless (no DbContext/EF packages, no Infrastructure reference)
  • Platform Support: Devices (MAUI) include EF Core + SQLite and publish successfully
  • CHANGELOG Updated: Added entry to [Unreleased] section in docs/CHANGELOG.md
  • Documentation: All changes emphasized on success in relevant documentation files

Code Quality & Testing

  • StyleCop formatting passes (dotnet format --verify-no-changes)
  • Roslynator analysis passes (roslynator analyze PTDoc.sln --severity-level info)
  • Unit tests pass for affected components
  • Cross-platform compatibility verified (Web, Android, iOS, macOS)

Build Artifacts

  • net8.0 (Web/API projects)
  • net8.0-android (Android MAUI)
  • net8.0-ios (iOS MAUI)
  • net8.0-maccatalyst (macOS MAUI)
  • Blazor Web App (hybrid Server/WASM deployment)

Functional Testing

  • Patient management operations (if applicable)
  • Assessment creation and editing (if applicable)
  • Authentication/authorization (if applicable)
  • Database operations (if applicable)
  • Cross-platform UI consistency
  • Performance: No significant degradation in startup time, responsiveness, or memory usage

Platform-Specific Validation

  • Android: APK builds and installs successfully
  • iOS: IPA builds (unsigned) successfully
  • Web: Blazor Web App loads and functions correctly
  • macOS: App builds via Catalyst successfully

Documentation & Communication

  • Changes documented in appropriate files (ARCHITECTURE.md, CHANGELOG.md, etc.)
  • Breaking changes clearly identified
  • User-facing changes explained
  • Developer impact documented (new services, APIs, components)

Testing Instructions

1. Setup

# Clone and checkout branch
git checkout Patient-Intake-To-Dos-sprint
./PTDoc-Foundry.sh

# Build solution
dotnet build PTDoc.sln -c Release

2. Functional Testing

# Run API
dotnet run --project src/PTDoc.Api --urls http://localhost:5170

# Run Web
dotnet run --project src/PTDoc.Web
# Navigate to http://localhost:5001/intake

Expected Behavior:

  • GET /api/v1/intake/patient/{id}/draft returns only unlocked drafts ordered by most-recently-modified
  • PUT /api/v1/intake/{id} with templateVersion longer than 50 characters returns 400 ValidationProblem
  • POST /api/v1/intake/{id}/submit with {"hipaaAcknowledged": false} returns 400 ValidationProblem
  • POST /api/v1/intake/{id}/submit with {"hipaaAcknowledged": true} succeeds and sets IsLocked = true
  • GET /api/v1/patients list response does not include dateOfBirth
  • Clinician patient selector fires a server-side search request after 300ms typing pause rather than filtering a pre-loaded list
  • Patient <select> dropdown is announced by screen readers via aria-label="Select patient"
  • ApiAccessTokenForwardingHandler forwards the bearer token in both local-auth and Entra External ID flows
  • Submitting an intake without a PatientId throws rather than silently succeeding
  • A locked-but-not-submitted intake loads as read-only in the wizard (not editable)
  • All CI jobs build and pass (no CS8300 merge conflict marker errors)

3. Accessibility Testing (if UI changes)

# Keyboard Navigation Test
# - Tab through search input and patient dropdown
# - Verify <select> is announced as "Select patient" by VoiceOver/NVDA

# Screen Reader Test (macOS)
# - Enable VoiceOver: Cmd+F5
# - Verify patient dropdown label is announced correctly

4. Cross-Platform Testing (if applicable)

# Android
dotnet build src/PTDoc.Maui/PTDoc.Maui.csproj -c Release -f net8.0-android

# iOS (requires Mac)
dotnet build src/PTDoc.Maui/PTDoc.Maui.csproj -c Release -f net8.0-ios

# macOS Catalyst
dotnet build src/PTDoc.Maui/PTDoc.Maui.csproj -c Release -f net8.0-maccatalyst

Verification Commands

# Code quality checks
dotnet format PTDoc.sln --verify-no-changes --verbosity diagnostic
roslynator analyze PTDoc.sln --severity-level info

# Build verification - All projects
dotnet build PTDoc.sln -c Release

# Build verification - Individual projects (if needed)
dotnet build src/PTDoc.Web/PTDoc.Web.csproj -c Release
dotnet build src/PTDoc.Api/PTDoc.Api.csproj -c Release

# Build verification - MAUI targets (if applicable)
dotnet publish src/PTDoc.Maui/PTDoc.Maui.csproj -c Release -f net8.0-android
dotnet publish src/PTDoc.Maui/PTDoc.Maui.csproj -c Release -f net8.0-ios
dotnet publish src/PTDoc.Maui/PTDoc.Maui.csproj -c Release -f net8.0-maccatalyst

# Test execution
dotnet test PTDoc.sln -c Release
dotnet test --filter "Category=Unit"

# Targeted test for CI-fixed tests
dotnet test tests/PTDoc.Tests/PTDoc.Tests.csproj --filter "UC1_PracticeManager|UC2_Intake|UC2_Locked|UC2_Submit"

Review Feedback

For Reviewers: Please comment on the following areas (check all that you verified):

Design & Usability (if UI changes)

  • Design Fidelity: Components match Figma specifications (if applicable)
  • Accessibility: Keyboard navigation and screen reader support work correctly
  • Responsive Design: Tested at multiple breakpoints (desktop/tablet/mobile)
  • Theme Support: Light/dark mode appearance is correct

Code Quality

  • Component Structure: Components are properly decomposed and reusable
  • Architecture: Adheres to Clean Architecture principles (correct layer boundaries)
  • Code Style: Follows project conventions and coding standards
  • Error Handling: Appropriate error handling and user feedback

Performance & Testing

  • Performance: Page load and interaction responsiveness is acceptable
  • Cross-Platform: Tested on at least 2 platforms (if applicable)
  • Test Coverage: Changes include appropriate unit/integration tests

Documentation

  • Code Comments: Complex logic is well-documented
  • User Documentation: User-facing changes are documented
  • Developer Documentation: Technical changes are explained for future maintainers

Additional Context

Security Summary: CodeQL scan reports no new alerts. PHI exposure reduced by removing DateOfBirth from the patient list DTO and server projection. HIPAA acknowledgement is now enforced server-side by parsing consent JSON before submission is accepted. Silent failure modes in SubmitAsync and CreateTemporaryPatientAndDraftIntakeAsync replaced with explicit exceptions to prevent incorrect UI state.

CI Fix Summary: All 12 CI check failures were caused by unresolved git merge conflict markers (<<<<<<< HEAD / ======= / >>>>>>>) left in tests/PTDoc.Tests/Security/PfptRoleComplianceTests.cs after the merge commit. The conflict was resolved by keeping the public void signature (correct for a synchronous Assert.Equal-only test). Additionally, SubmitIntake was changed from private to internal to allow direct handler invocation from the test assembly, and the UC2_IntakeForm_LocksAfterSubmit_ViaRealHandler test fixture was updated to include hipaaAcknowledged: true in its consents payload.


Healthcare Context Reminder: PTDoc is a HIPAA-conscious application. Ensure all patient data handling includes:

  • Audit trails for data access/modifications
  • Secure session management
  • Appropriate authentication/authorization checks
  • No sensitive data in logs or error messages

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Add server endpoints to support intake drafts and locking: GET /intake/patient/{id}/draft, PUT /intake/{id}, POST /intake/{id}/submit and POST /intake/{id}/lock. Add a patient listing endpoint for clinician workflows and new DTOs (UpdateIntakeRequest, PatientListItemResponse). Persist and respect IsLocked/IsSubmitted state across services and the mock implementation. Introduce a Blazor ClinicianPatientSelector component and wire an IntakeApiService to call the new endpoints; update the intake wizard UI/state to handle locked intakes and improve error handling. Add PTDocClaimTypes.ApiAccessToken and an ApiAccessTokenForwardingHandler to forward the API token from the authenticated web principal to outgoing ServerAPI HTTP requests, registering the handler with the HttpClient factory. Minor plumbing updates to create and propagate the API access token claim when signing in.
@BlackHouseDeveloper BlackHouseDeveloper marked this pull request as ready for review March 28, 2026 00:30
Copilot AI review requested due to automatic review settings March 28, 2026 00:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces end-to-end intake draft locking/submission support, adds a clinician-oriented patient listing/selector flow in the UI, and forwards an API access token on ServerAPI HttpClient calls from PTDoc.Web to PTDoc.Api.

Changes:

  • Added new Intake endpoints (draft-by-patient, update, submit, lock) and a clinician patient list endpoint + DTOs.
  • Updated UI intake wizard state/flow to respect locked/submitted drafts, and introduced a clinician patient selector component.
  • Implemented API access token claim + forwarding handler and wired it into PTDoc.Web’s HttpClientFactory.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
src/PTDoc.Web/Services/ApiAccessTokenForwardingHandler.cs Adds a delegating handler to forward a bearer token on outbound ServerAPI calls.
src/PTDoc.Web/Program.cs Registers the forwarding handler/HttpClient pipeline, switches intake service to API-backed implementation, and adds the API token claim on sign-in.
src/PTDoc.UI/Services/IntakeApiService.cs Implements IIntakeService via new Intake API endpoints (create/update/submit, draft retrieval).
src/PTDoc.UI/Pages/Intake/IntakeWizardPage.razor Updates intake wizard UX/state handling for locked/submitted drafts and adds clinician patient selection UI.
src/PTDoc.UI/Components/Intake/Models/IntakeWizardState.cs Adds IsSubmitted and IsLocked wizard state flags.
src/PTDoc.UI/Components/Intake/ClinicianPatientSelector.razor New component that fetches and filters a clinician patient list for intake selection.
src/PTDoc.Infrastructure/Services/MockIntakeService.cs Updates mock intake behavior to respect lock/submission state.
src/PTDoc.Application/Services/IntakeResponseDraft.cs Adds IsLocked to the draft contract.
src/PTDoc.Application/Identity/PTDocClaimTypes.cs Adds ApiAccessToken claim type.
src/PTDoc.Application/DTOs/PatientDtos.cs Adds PatientListItemResponse projection DTO for clinician selection UI.
src/PTDoc.Application/DTOs/IntakeDtos.cs Adds UpdateIntakeRequest DTO for updating an existing intake draft.
src/PTDoc.Api/Patients/PatientEndpoints.cs Adds GET /api/v1/patients list endpoint for clinician workflows.
src/PTDoc.Api/Intake/IntakeEndpoints.cs Adds draft-by-patient, update, submit, and lock endpoints and maps lock/submission behavior.

public Guid Id { get; set; }
public string DisplayName { get; set; } = string.Empty;
public string? MedicalRecordNumber { get; set; }
public DateTime DateOfBirth { get; set; }
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PatientListItemResponse includes DateOfBirth, but the clinician selector UI only needs an identifier + display label (and possibly MRN). Returning DOB in a “lightweight projection” increases PHI exposure unnecessarily; remove it unless a concrete UI need exists.

Suggested change
public DateTime DateOfBirth { get; set; }

Copilot uses AI. Check for mistakes.
Comment on lines +165 to +171
intake.PainMapData = string.IsNullOrWhiteSpace(request.PainMapData) ? "{}" : request.PainMapData;
intake.Consents = string.IsNullOrWhiteSpace(request.Consents) ? "{}" : request.Consents;
intake.ResponseJson = string.IsNullOrWhiteSpace(request.ResponseJson) ? "{}" : request.ResponseJson;
intake.TemplateVersion = string.IsNullOrWhiteSpace(request.TemplateVersion) ? "1.0" : request.TemplateVersion;
intake.LastModifiedUtc = DateTime.UtcNow;
intake.ModifiedByUserId = identityContext.GetCurrentUserId();
intake.SyncState = SyncState.Pending;
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpdateIntakeRequest.TemplateVersion has [MaxLength(50)], but Minimal API endpoints don’t automatically run DataAnnotations validation. UpdateIntake also doesn’t enforce the length, so an overlong template version can lead to DB errors at save time. Add explicit validation (similar to CreatePatient) or an endpoint filter for DTO validation.

Copilot uses AI. Check for mistakes.
Comment on lines +36 to 55
group.MapGet("/patient/{patientId:guid}/draft", GetDraftByPatient)
.WithName("GetPatientDraftIntake")
.WithSummary("Get most recent intake draft for a patient")
.RequireAuthorization(AuthorizationPolicies.IntakeRead);

group.MapPut("/{id:guid}", UpdateIntake)
.WithName("UpdateIntake")
.WithSummary("Update an existing intake draft")
.RequireAuthorization(AuthorizationPolicies.IntakeWrite);

group.MapPost("/{id:guid}/submit", SubmitIntake)
.WithName("SubmitIntake")
.WithSummary("Submit and lock an intake response")
.RequireAuthorization(AuthorizationPolicies.IntakeWrite);

group.MapPost("/{id:guid}/lock", LockIntake)
.WithName("LockIntake")
.WithSummary("Lock an intake response")
.RequireAuthorization(AuthorizationPolicies.IntakeWrite);
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New API behavior is being introduced (draft lookup by patient, update, submit, lock, and patient listing) without corresponding automated tests. Add integration tests covering authorization, tenant scoping, and lock/submit invariants (e.g., update returns 409 when locked; submit sets IsLocked/SubmittedAt; list endpoint returns only tenant-scoped patients).

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +41 to +45
var createPatientRequest = new CreatePatientRequest
{
FirstName = firstName,
LastName = lastName,
DateOfBirth = state.DateOfBirth ?? new DateTime(1970, 1, 1),
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateTemporaryPatientAndDraftIntakeAsync falls back to new DateTime(1970, 1, 1) when state.DateOfBirth is null. Since DOB is required by IntakeDemographicsValidationService and the patient API rejects default DOBs, this risks creating incorrect patient data if the caller bypasses validation. Prefer throwing/returning a validation failure when DateOfBirth is missing instead of inventing a date.

Suggested change
var createPatientRequest = new CreatePatientRequest
{
FirstName = firstName,
LastName = lastName,
DateOfBirth = state.DateOfBirth ?? new DateTime(1970, 1, 1),
if (state.DateOfBirth is null)
{
throw new ArgumentException("Date of birth is required to create a temporary patient.", nameof(state));
}
var createPatientRequest = new CreatePatientRequest
{
FirstName = firstName,
LastName = lastName,
DateOfBirth = state.DateOfBirth.Value,

Copilot uses AI. Check for mistakes.
id="intake-patient-search"
type="search"
class="intake-patient-selector__search"
placeholder="Search by name, MRN, or email"
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The search input placeholder says "Search by name, MRN, or email", but the component only filters by DisplayName and MedicalRecordNumber (no email is loaded or searched). Either include email in the list/search logic or adjust the placeholder text to match actual behavior.

Suggested change
placeholder="Search by name, MRN, or email"
placeholder="Search by name or MRN"

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +90
var updateRequest = new
{
painMapData = BuildPainMapJson(state),
consents = BuildConsentJson(state),
responseJson = JsonSerializer.Serialize(state, SerializerOptions),
templateVersion = "1.0"
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SaveDraftAsync uses an anonymous object for the PUT body even though UpdateIntakeRequest was added to PTDoc.Application.DTOs. Using the shared DTO here would reduce drift risk (property names/defaults) and gives compile-time validation if the API contract changes.

Suggested change
var updateRequest = new
{
painMapData = BuildPainMapJson(state),
consents = BuildConsentJson(state),
responseJson = JsonSerializer.Serialize(state, SerializerOptions),
templateVersion = "1.0"
var updateRequest = new UpdateIntakeRequest
{
PainMapData = BuildPainMapJson(state),
Consents = BuildConsentJson(state),
ResponseJson = JsonSerializer.Serialize(state, SerializerOptions),
TemplateVersion = "1.0"

Copilot uses AI. Check for mistakes.
}

draft.PatientId = response.PatientId;
draft.IsSubmitted = response.SubmittedAt.HasValue;
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ToDraft(IntakeResponse response) sets IsSubmitted but never maps the server-side lock state (response.Locked) into IntakeResponseDraft.IsLocked. This will cause locked (but not submitted) intakes to load as editable in the wizard until the API rejects updates with 409. Map draft.IsLocked from the response as well.

Suggested change
draft.IsSubmitted = response.SubmittedAt.HasValue;
draft.IsSubmitted = response.SubmittedAt.HasValue;
draft.IsLocked = response.Locked;

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +111
return;
}

await SaveDraftAsync(state, cancellationToken);

var existing = await GetIntakeByPatientAsync(state.PatientId.Value, cancellationToken);
if (existing is null || existing.Locked)
{
return;
}

Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SubmitAsync returns without error when existing is null or already locked. The calling UI treats a successful return as “submitted” and locks the wizard, so this silent no-op can produce incorrect UI state. Consider throwing a specific exception or returning a result indicating the intake was not submitted (e.g., not found / already locked).

Suggested change
return;
}
await SaveDraftAsync(state, cancellationToken);
var existing = await GetIntakeByPatientAsync(state.PatientId.Value, cancellationToken);
if (existing is null || existing.Locked)
{
return;
}
throw new InvalidOperationException("Cannot submit intake because PatientId is missing.");
}
await SaveDraftAsync(state, cancellationToken);
var existing = await GetIntakeByPatientAsync(state.PatientId.Value, cancellationToken);
if (existing is null)
{
throw new InvalidOperationException($"Cannot submit intake; no draft intake found for patient {state.PatientId.Value}.");
}
if (existing.Locked)
{
throw new InvalidOperationException($"Cannot submit intake; intake {existing.Id} is already locked.");
}

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +38
<select
id="intake-patient-select"
class="intake-patient-selector__select"
value="@SelectedPatientIdString"
@onchange="HandleSelectionChangedAsync"
disabled="@Disabled">
<option value="">Select a patient</option>
@foreach (var patient in FilteredPatients)
{
<option value="@patient.Id">@FormatPatientOption(patient)</option>
}
</select>
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <select> has no associated label or aria-label (the only <label> points at the search input). This makes the patient dropdown hard to use with screen readers. Add a label tied to intake-patient-select, or give the <select> an accessible name via aria-label/aria-labelledby.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +64
protected override async Task OnInitializedAsync()
{
try
{
var result = await HttpClient.GetFromJsonAsync<List<PatientListItem>>("/api/v1/patients?take=250");
if (result is not null)
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnInitializedAsync always loads up to 250 patients and then filters client-side. Since the API supports query, this can be slow for large clinics and defeats server-side search. Consider calling /api/v1/patients?query=... with debouncing/paging rather than eagerly downloading a large list.

Copilot uses AI. Check for mistakes.
@BlackHouseDeveloper
Copy link
Copy Markdown
Owner Author

@copilot apply changes based on the comments in this thread

…lity and server-side search

Agent-Logs-Url: https://github.com/BlackHouseDeveloper/PTDoc/sessions/781d35c2-8188-415b-b2ef-6f20f4bc5d7b

Co-authored-by: BlackHouseDeveloper <20420616+BlackHouseDeveloper@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 28, 2026

@copilot apply changes based on the comments in this thread

Applied all changes from the review thread in commit d5c8992:

  • PHI reduction: Removed DateOfBirth from PatientListItemResponse DTO and ListPatients projection
  • GetDraftByPatient: Now filters to !IsLocked only and orders by LastModifiedUtc desc
  • UpdateIntake validation: Added explicit TemplateVersion max-length (50) check (DataAnnotations aren't auto-applied in Minimal APIs)
  • SubmitIntake HIPAA validation: Parses consent JSON and requires hipaaAcknowledged: true server-side
  • CreateTemporaryPatientAndDraftIntakeAsync: Throws ArgumentException when DateOfBirth is null instead of defaulting to 1970
  • SaveDraftAsync: Uses typed UpdateIntakeRequest DTO instead of anonymous object
  • SubmitAsync: Throws InvalidOperationException when intake is not found or already locked (no more silent no-op)
  • ToDraft: Maps IsLocked from the API response
  • ClinicianPatientSelector: Placeholder updated to "Search by name or MRN"; <select> gets aria-label="Select patient"; replaced eager 250-patient load with server-side search + 300ms debounce using a reusable timer
  • ApiAccessTokenForwardingHandler: Falls back to HttpContext.GetTokenAsync("access_token") (OIDC saved token) when the PTDocClaimTypes.ApiAccessToken claim is absent

@BlackHouseDeveloper BlackHouseDeveloper marked this pull request as draft March 28, 2026 00:52
@BlackHouseDeveloper BlackHouseDeveloper marked this pull request as ready for review March 28, 2026 00:58
@BlackHouseDeveloper
Copy link
Copy Markdown
Owner Author

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 28, 2026

@copilot apply changes based on the comments in this thread

All changes from the review thread were already applied in commit d5c8992. The codebase currently has all of the following in place:

  • DateOfBirth removed from PatientListItemResponse and ListPatients projection
  • GetDraftByPatient filters !IsLocked and orders by LastModifiedUtc DESC
  • UpdateIntake validates TemplateVersion max-length (50) explicitly
  • SubmitIntake parses consent JSON and enforces hipaaAcknowledged: true
  • CreateTemporaryPatientAndDraftIntakeAsync throws ArgumentException on null DateOfBirth
  • SaveDraftAsync uses typed UpdateIntakeRequest DTO
  • SubmitAsync throws InvalidOperationException on null/locked intake
  • ToDraft maps IsLocked from the response
  • ClinicianPatientSelector placeholder is "Search by name or MRN", <select> has aria-label="Select patient", and search uses server-side API with 300ms debounce
  • ApiAccessTokenForwardingHandler falls back to GetTokenAsync("access_token") for the OIDC flow

Copilot AI changed the title Add intake locking, patient list, and API client Add intake locking, patient list, API client, and security hardening Mar 28, 2026
@BlackHouseDeveloper
Copy link
Copy Markdown
Owner Author

@copilot it failed 12 checks. figure out if the changes were ci breaking or if they actually failed those tests the way it was intended. Obce the problems are found implement the resolution

…e as internal

- Remove unresolved merge conflict markers from PfptRoleComplianceTests.cs (line 196-200) — these caused all 12 CI check failures with CS8300
- Change SubmitIntake from private to internal in IntakeEndpoints.cs so the test project can call it directly via InternalsVisibleTo
- Add Consents = hipaaAcknowledged:true to UC2_IntakeForm_LocksAfterSubmit test to satisfy the HIPAA validation added in the previous commit

Agent-Logs-Url: https://github.com/BlackHouseDeveloper/PTDoc/sessions/c6222c87-da01-4586-9f0d-b2f42be8c849

Co-authored-by: BlackHouseDeveloper <20420616+BlackHouseDeveloper@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 28, 2026

@copilot it failed 12 checks. figure out if the changes were ci breaking or if they actually failed those tests the way it was intended. Obce the problems are found implement the res...

All 12 CI failures had the same root cause: an unresolved git merge conflict marker left in tests/PTDoc.Tests/Security/PfptRoleComplianceTests.cs (lines 196–200) from the merge commit. This caused CS8300 build errors in every workflow that builds the test project.

Fixed in commit e5a458a with three changes:

  1. Resolved the conflict — kept public void (correct; the method body has no await, so async Task was unnecessary)
  2. Changed SubmitIntake to internal in IntakeEndpoints.cs — the tests call the handler directly via the existing [assembly: InternalsVisibleTo("PTDoc.Tests")], but it was private so the compiler rejected the call
  3. Updated UC2_IntakeForm_LocksAfterSubmit_ViaRealHandler — added Consents = "{\"hipaaAcknowledged\":true}" to the test's intake fixture so it satisfies the HIPAA validation added in the previous commit (without it the test would get a 400 instead of 200 OK)

All 5 affected tests now pass locally.

Copilot AI changed the title Add intake locking, patient list, API client, and security hardening Add intake locking, patient list, API client, security hardening, and CI fix Mar 28, 2026
@BlackHouseDeveloper BlackHouseDeveloper merged commit e9f4a7d into Backend-Integration-Completion Mar 28, 2026
24 checks passed
@BlackHouseDeveloper BlackHouseDeveloper deleted the Patient-Intake-To-Dos-sprint branch March 28, 2026 01:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants