Skip to content

feat: scene-based PA demo with cinematic transitions#35

Open
rsalus wants to merge 28 commits intomainfrom
feat/pa-dashboard-demo
Open

feat: scene-based PA demo with cinematic transitions#35
rsalus wants to merge 28 commits intomainfrom
feat/pa-dashboard-demo

Conversation

@rsalus
Copy link
Contributor

@rsalus rsalus commented Mar 15, 2026

Summary

Three-scene demo experience for PA workflow, targeting both clinician and investor audiences:

  • Scene 1 — "The Encounter": Enhanced athenaOne EHR mock with chart tabs (Problems, Meds, Allergies, Vitals, Imaging, Labs), Authorization Determination Engine notification, and expanded state machine (chart-browsing → order-entry → pa-detected → documenting → existing PA flow)
  • Scene 2 — "The Command Center": Palantir-inspired fleet dashboard with 6 animated KPI cards, 48-case fleet grid with status filtering, case pipeline with flowing dots, and GraphQL wiring with seed data fallback
  • Scene 3 — "The Case Detail": React Flow node graph visualizing AI reasoning (Patient → Evidence → Criteria → Decision) with animated edges, vertical case timeline, expandable criteria evidence, and clinical summary
  • Navigation: Pill-style scene selector with auto-play mode (15s cycling) and reset. Cinematic transitions between scenes via Motion (framer-motion successor)
  • Backend: Deterministic seed script generating 48 mock PA requests across 6 statuses, 7 patients, 3 payers, 3 procedure types

Changes

New Components (17 files)

  • components/demo/ — DemoProvider context, SceneNav, SceneTransition, transition variants
  • components/fleet/ — KPICards, FleetCard, FleetView, CasePipeline
  • components/case/ — CaseGraph, CaseTimeline, PatientNode, EvidenceNode, CriteriaNode, DecisionNode, AnimatedEdge, NodeCard
  • components/ehr/ — AuthDetectionBanner, ChartTabPanel

Enhanced Components (5 files)

  • EhrHeader.tsx — encounter type pill, facility name
  • EncounterSidebar.tsx — chart tab navigation + PA stage tracking
  • useEhrDemoFlow.ts — 5 new states, backward compatible
  • ehr-demo.tsx — DemoProvider + SceneNav integration, "View in Fleet" transition

New Routes

  • /fleet — Fleet dashboard page
  • /case/$caseId — Case detail drilldown

New Dependencies

  • motion ^12 (page transitions, micro-animations)
  • @xyflow/react ^12 (node graph visualization)

Backend

  • SeedDemoData.cs — 48 deterministic mock PA requests for PostgreSQL

Test Plan

  • 404 tests passing (95+ new, 12 existing ehr-demo preserved)
  • All new components have test files with behavioral assertions
  • State machine tests cover both legacy (idle start) and enhanced (chart-browsing start) flows
  • CaseGraph graph data builder unit tested separately from rendering
  • Fleet seed data validates distribution, patient coverage, payer variety, confidence range
  • Backend seed tests (TUnit): 7 tests covering count, distribution, patients, procedures, payers, confidence, timestamps
  • Zero TypeScript errors
  • Spec review: PASS
  • Code quality review: PASS (SOLID, security, test quality, operational)

🤖 Generated with Claude Code

rsalus and others added 27 commits March 15, 2026 02:15
- Add Dashboard Dockerfile (multi-stage Node build → nginx) and nginx.conf
  with SPA routing and /api reverse proxy to Gateway
- Simplify GraphQL client to use relative /api/graphql (works with both
  Vite dev proxy and nginx prod proxy)
- Update AppHost with IsPublishMode conditional: Vite dev server for local,
  containerized nginx for Azure deployment
- Add Azure hosting NuGet packages for PostgreSQL and Redis
- Generate azd infrastructure (Bicep + ACA templates) with .Env references
  for parameter resolution
- Add azd-sync-secrets.sh to sync dotnet user-secrets → azd env vars

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ase state

Install motion + @xyflow/react deps. Create DemoProvider context that manages
scene navigation (encounter/fleet/case) and selected case ID state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Horizontal pill-style nav bar with Encounter/Fleet/Case Detail buttons.
Active pill shows teal fill, inactive shows outline. Includes Reset Demo button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… scaffolding

Add SceneTransition component using motion/react AnimatePresence for opacity
fade transitions between scenes. Scaffold /fleet and /case/$caseId routes
with SceneNav integration and placeholder content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add SeedDemoData static class that generates 48 deterministic PARequestModel
instances for the PA dashboard demo, distributed across all 7 Athena sandbox
patients with realistic clinical content, procedures (MRI Lumbar, MRI Cervical,
CT Abdomen/Pelvis), and status distribution (6 processing, 8 ready,
15 submitted, 8 waiting_for_insurance, 9 approved, 2 denied).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ponent

Add deterministic fleet seed data generator (48 PA requests across 6 statuses)
and KPI cards component with color-coded borders and active filter highlighting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…anel component

Add DEMO_CHART_DATA with problems (ICD codes), medications, allergies (NKDA),
imaging history, and lab results. Create ChartTabPanel component that renders
tab-specific content for the sidebar chart browser.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add compact FleetCard with patient initials, status dot, CPT code, payer badge,
and confidence display. FleetView provides a filterable grid with highlight support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add PatientNode, EvidenceNode, CriteriaNode, DecisionNode components
with shared NodeCard wrapper. Each node has typed data props, styled
borders, and React Flow handles for graph connectivity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add horizontal pipeline visualization with 6 stages (Order Signed through Payer
Response), interactive counts, active stage highlighting, and animated SVG connectors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hart tabs

Add chart tab browser (Problems, Meds, Allergies, Vitals, Imaging, Labs) to
the sidebar when chartData is provided. Enhanced mode shows extended encounter
stages (Intake through Sign) and optional PA detection stages. Legacy mode
(no chartData) preserves all existing behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CaseGraph component that builds a React Flow visualization from
PARequest data (patient -> evidence -> criteria -> decision). Includes
AnimatedEdge with SVG animateMotion dot and buildCaseGraphData utility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…aphQL wiring

Assemble the fleet dashboard page with KPI cards, case pipeline, fleet grid,
and activity feed sidebar. Falls back to seed data when GraphQL is unavailable.
KPI card clicks filter the fleet view by status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…chine

Add pre-flagged states (chart-browsing, order-entry, pa-detected, documenting)
and new actions (addOrder, startDocumenting, transitionToFleet) to support the
enhanced Scene 1 demo flow. order-entry auto-transitions to pa-detected after
1200ms. Default startState='idle' preserves all existing behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	apps/dashboard/src/routeTree.gen.ts
#	apps/dashboard/src/routes/fleet.tsx
…nd EhrHeader enhancements

Add AuthDetectionBanner component with amber/teal accent, payer name, and
policy reference (LCD L34220). Enhance EhrHeader with encounter type pill
badge and optional facility name. All existing behavior preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CaseTimeline component with completed/active/pending phase rendering.
Implement full case.$caseId route with split-panel layout: evidence graph
(left) and detail cards (right) including timeline, patient summary,
expandable criteria evidence, and clinical summary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	apps/dashboard/src/routeTree.gen.ts
#	apps/dashboard/src/routes/case.$caseId.tsx
…yout animations

Add DemoProvider (context with autoPlay cycling, resetDemo), SceneTransition
(AnimatePresence wrapper with direction-based variants), SceneNav (pill navigation
with auto-play toggle), and extracted transitionVariants. 9 new tests covering
scene switching, auto-play cycling, non-linear navigation, and state reset.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…y point

Wire DemoProvider + SceneNav into ehr-demo page, add "View in Fleet" button
after PA submission completes, create stub fleet and case route files with
DemoProvider/SceneNav wiring. All 12 original ehr-demo tests preserved,
2 new integration tests added (14 total).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion

Resolve merge conflicts between parallel agent streams:
- Take integration agent's enhanced DemoProvider (autoPlay, resetDemo), SceneNav (auto-play toggle), SceneTransition (direction variants)
- Keep fleet/case-detail agents' full route implementations
- Add DemoProvider wrapping to fleet and case routes
- Replace case route's local SceneNav with shared component
- Regenerate route tree

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…entation

The integration agent used aria-current="page" instead of data-active for
active scene indication (better accessibility). Update tests to match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

📝 Walkthrough

Walkthrough

Introduces a three‑scene Prior Authorization dashboard (Enhanced EHR, Fleet, Case Detail) with demo orchestration, React Flow graph visualizations, new UI components and hooks, deterministic fleet seed data and backend seed generator, containerized dashboard (Docker + Nginx), Azure infra templates, and broad test coverage and route additions.

Changes

Cohort / File(s) Summary
Docker & Nginx / Orchestration
apps/dashboard/Dockerfile, apps/dashboard/nginx.conf, orchestration/AuthScript.AppHost/..., orchestration/AuthScript.AppHost/infra/*, orchestration/AuthScript.AppHost/manifest.json
Adds multi‑stage dashboard Dockerfile and nginx config; AppHost publish-mode container wiring; Azure Container Apps Bicep/YAML templates, Dockerfiles for services, infra manifests, and azd sync helpers.
Demo framework & navigation
apps/dashboard/src/components/demo/*, apps/dashboard/src/components/demo/__tests__/*, apps/dashboard/src/routes/__root.tsx
Adds DemoProvider (scene state, autoplay), SceneNav, SceneTransition, transitionVariants, tests, and wraps root outlet with DemoProvider.
Case graph, nodes & timeline
apps/dashboard/src/components/case/*, apps/dashboard/src/components/case/__tests__/*
Implements CaseGraph, custom React Flow nodes (Patient/Evidence/Criteria/Decision), AnimatedEdge, NodeCard, CaseTimeline, buildCaseGraphData, plus unit tests.
Fleet UI & pages
apps/dashboard/src/components/fleet/*, apps/dashboard/src/components/fleet/__tests__/*, apps/dashboard/src/routes/fleet.tsx
Adds FleetPage route, KPICards, CasePipeline, FleetCard, FleetView, pipeline/KPI interactions, activity feed, and tests.
EHR enhancements & flow
apps/dashboard/src/components/ehr/*, apps/dashboard/src/components/ehr/__tests__/*, apps/dashboard/src/components/ehr/useEhrDemoFlow.ts
ChartTabPanel, AuthDetectionBanner, EncounterSidebar enhancements (chartData, paDetected), expanded useEhrDemoFlow states and actions, and corresponding tests.
Case detail route & UI
apps/dashboard/src/routes/case.$caseId.tsx, apps/dashboard/src/routes/__tests__/case.test.tsx
New CaseDetailPage route composing CaseGraph, CaseTimeline, CriteriaSection, patient summary, timeline builder, and tests.
Seed data & lib
apps/dashboard/src/lib/*, apps/gateway/Gateway.API/Data/SeedDemoData.cs, apps/gateway/Gateway.API.Tests/*
Adds DEMO_CHART_DATA, generateFleetData(), and backend SeedDemoData generator + tests to produce deterministic 48‑item fleet dataset and chart demo data.
GraphQL client & package
apps/dashboard/src/api/graphqlClient.ts, apps/dashboard/package.json, package.json
Unifies GraphQL endpoint to relative /api/graphql, removes getApiConfig usage; adds dashboard package deps and updates build:containers to build dashboard image.
Utilities & formatting
apps/dashboard/src/lib/formatUtils.ts, apps/dashboard/src/lib/__tests__/*
Adds getInitials and getConfidenceColor helpers and tests.
Tests & test helpers
apps/dashboard/src/**/__tests__/*
Comprehensive unit and integration tests across demo, components, routes, seed data, and utilities; many mocks for motion, @xyflow/react, and router.
Docs & plans
docs/designs/*, docs/plans/*, docs/market-research/*, orchestration/AuthScript.AppHost/next-steps.md
Design docs and implementation plans for the three‑scene dashboard, Azure deployment guide, and market research mockups.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SceneNav
    participant DemoProvider
    participant SceneTransition
    participant Scene as SceneComponent

    User->>SceneNav: click scene pill
    SceneNav->>DemoProvider: setScene(key)
    DemoProvider->>DemoProvider: update scene state
    DemoProvider-->>SceneTransition: new sceneKey
    SceneTransition->>Scene: render (initial → animate)
    Scene-->>User: scene displayed
Loading
sequenceDiagram
    participant Client
    participant FleetPage
    participant GraphQL
    participant SeedGen as generateFleetData
    participant KPICards
    participant FleetView

    Client->>FleetPage: render /fleet
    FleetPage->>GraphQL: request PARequests/Stats
    alt GraphQL returns data
        GraphQL-->>FleetPage: data
    else GraphQL unavailable
        FleetPage->>SeedGen: generateFleetData()
        SeedGen-->>FleetPage: fallback data
    end
    FleetPage->>KPICards: pass stats, onFilter
    KPICards->>FleetPage: onFilter('ready')
    FleetPage->>FleetView: provide filtered requests
    FleetView-->>Client: render filtered cards
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🧭 Three scenes spin on a demo stage,
Nodes and edges tell the AI's page,
KPIs pulse, pipelines glide,
Charts and tabs walk side by side,
Docker hums, infra lights the stage.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/pa-dashboard-demo

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard/src/components/ehr/EncounterSidebar.tsx (1)

48-62: ⚠️ Potential issue | 🟠 Major

Enhanced mode still uses the legacy PA visibility/index logic.

With startState='chart-browsing', showPAStages is already true, so the enhanced sidebar renders Prior Auth before paDetected is set. The same legacy index mapping also mislabels the 2-step enhanced flow: reviewing lights up PA Submit, and submitting marks both stages completed. The enhanced path needs its own gating and active-step mapping instead of reusing the legacy one.

Also applies to: 191-195, 254-267

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/ehr/EncounterSidebar.tsx` around lines 48 - 62,
The enhanced sidebar is incorrectly reusing the legacy PA visibility/index logic
(getPAActiveIndex), causing Prior Auth to render before paDetected and
mis-mapping the 2-step enhanced flow; add a separate mapping and gating for the
enhanced path (e.g., create getPAEnhancedActiveIndex(flowState: EhrDemoState)
and a boolean gate like shouldShowPAEnhanced(startState, paDetected) or use an
isEnhanced flag) and replace legacy usages in the enhanced rendering branches
(where getPAActiveIndex is currently used; also update the other occurrences
referenced in the review) so that the enhanced flow only shows PA stages after
paDetected and maps 'reviewing'/'submitting' to the correct 2-step
active/completed indices.
🟡 Minor comments (21)
docs/market-research/palantir-dashboard.md-8-9 (1)

8-9: ⚠️ Potential issue | 🟡 Minor

Fix markdown formatting inconsistency.

Line 8 contains an errant > blockquote character and excessive spacing (> * Left:) that breaks the list formatting. Additionally, the bullet style is inconsistent—line 8 uses * while line 9 uses -.

📝 Proposed formatting fix
-**Top Half:** >     * Left: App Logo (Blue square with "PA"), App Name "Prior Auth Hub", and subtitle "Meridian Health System" in muted text.
-  - Right: A search bar ("Search cases, patients, providers..."), a green dot with "System Operational", a "Notifications" button, and a user avatar square with "DR".
+**Top Half:**
+  - Left: App Logo (Blue square with "PA"), App Name "Prior Auth Hub", and subtitle "Meridian Health System" in muted text.
+  - Right: A search bar ("Search cases, patients, providers..."), a green dot with "System Operational", a "Notifications" button, and a user avatar square with "DR".

As per coding guidelines: docs/** → Check for accuracy and completeness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/market-research/palantir-dashboard.md` around lines 8 - 9, Remove the
stray blockquote character and extra spaces in the "Top Half" list item and make
the bullet style consistent across the two lines; specifically, change ">     *
Left: ..." to a normal list entry (remove the leading ">") and use the same
bullet character for both entries (either "-" or "*") with consistent
indentation so the "Left:" and "Right:" subitems render as a proper list in
palantir-dashboard.md.
orchestration/AuthScript.AppHost/infra/main.parameters.json-17-21 (1)

17-21: ⚠️ Potential issue | 🟡 Minor

Double AZURE_ prefix in environment variable names.

AZURE_AZURE_OPENAI_ENDPOINT and AZURE_AZURE_OPENAI_KEY appear to have a redundant prefix. If the convention is AZURE_<PARAM_NAME>, these should likely be AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_KEY respectively.

Suggested fix
       "azure_openai_endpoint": {
-        "value": "${AZURE_AZURE_OPENAI_ENDPOINT}"
+        "value": "${AZURE_OPENAI_ENDPOINT}"
       },
       "azure_openai_key": {
-        "value": "${AZURE_AZURE_OPENAI_KEY}"
+        "value": "${AZURE_OPENAI_KEY}"
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/main.parameters.json` around lines 17
- 21, The parameter values for azure_openai_endpoint and azure_openai_key use
duplicated AZURE_ prefixes; update the value expressions to reference the
correct environment variables AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_KEY
(replace "${AZURE_AZURE_OPENAI_ENDPOINT}" with "${AZURE_OPENAI_ENDPOINT}" and
"${AZURE_AZURE_OPENAI_KEY}" with "${AZURE_OPENAI_KEY}") so the keys match the
intended AZURE_<PARAM_NAME> convention.
docs/designs/2026-03-15-pa-dashboard-demo.md-17-28 (1)

17-28: ⚠️ Potential issue | 🟡 Minor

Add languages to fenced code blocks to satisfy markdownlint (MD040).

Several fenced sections are missing language identifiers; use text for diagrams/pseudocode blocks.

📝 Example fix pattern
-```
+```text
 Scene 1: "The Encounter"          Scene 2: "The Command Center"       Scene 3: "The Case Detail"
 ...
-```
+```
As per coding guidelines, `docs/**`: "Check for accuracy and completeness. Light review only."

Also applies to: 115-117, 182-185, 282-289, 315-319, 398-433

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/designs/2026-03-15-pa-dashboard-demo.md` around lines 17 - 28, The
fenced diagram blocks (e.g., the Scene 1/Scene 2/Scene 3 ASCII diagram
containing "Scene 1: \"The Encounter\"" and other similar pseudocode/diagram
blocks) are missing a language identifier and thus violate markdownlint MD040;
update each such code fence to include a language tag `text` (for example
convert ``` to ```text) for all diagram/pseudocode fenced blocks referenced
(including the block starting with Scene 1 and the other blocks noted in the
comment) so the docs pass linting while preserving the ASCII content.
orchestration/AuthScript.AppHost/next-steps.md-48-49 (1)

48-49: ⚠️ Potential issue | 🟡 Minor

Fix wording typo in the infrastructure note.

Line 48 reads “named manifests next the project file”; it should be “next to the project file.”

✏️ Proposed doc fix
-In addition, for each project resource referenced by your app host, a `containerApp.tmpl.yaml` file will be created in a directory named `manifests` next the project file. This file contains the infrastructure as code for running the project on Azure Container Apps.
+In addition, for each project resource referenced by your app host, a `containerApp.tmpl.yaml` file will be created in a directory named `manifests` next to the project file. This file contains the infrastructure as code for running the project on Azure Container Apps.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/next-steps.md` around lines 48 - 49, Fix the
wording typo in next-steps.md: change the phrase "named `manifests` next the
project file" to "named `manifests` next to the project file" so the sentence
reads that the `containerApp.tmpl.yaml` file is created in a directory named
`manifests` next to the project file; update the sentence in the paragraph
containing `containerApp.tmpl.yaml` and `manifests` to reflect this corrected
wording.
apps/dashboard/src/components/fleet/KPICards.tsx-77-80 (1)

77-80: ⚠️ Potential issue | 🟡 Minor

Set an explicit button type to prevent accidental form submission.

Line 77 should include type="button" for safe embedding in form contexts.

✅ Proposed fix
           <button
+            type="button"
             key={card.key}
             data-testid={`kpi-card-${card.key}`}
             onClick={() => onFilter(card.key)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/KPICards.tsx` around lines 77 - 80, The
button in KPICards.tsx that renders each card (the element using key={card.key},
data-testid={`kpi-card-${card.key}`} and onClick={() => onFilter(card.key)})
should explicitly set type="button" to avoid accidental form submission when
this component is used inside a form; update the button element to include the
type="button" attribute next to its existing props.
apps/dashboard/src/components/case/CaseTimeline.tsx-22-27 (1)

22-27: ⚠️ Potential issue | 🟡 Minor

Use a collision-resistant key for mapped phases.

Line 26 uses key={phase.name}; duplicate phase names will produce unstable reconciliation.

🛠️ Proposed key fix
-      {phases.map((phase, i) => {
+      {phases.map((phase, i) => {
         const isLast = i === phases.length - 1;
 
         return (
-          <div key={phase.name} className="flex gap-3">
+          <div key={`${phase.name}-${i}`} className="flex gap-3">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/CaseTimeline.tsx` around lines 22 - 27,
The mapped phases in CaseTimeline (phases.map inside the CaseTimeline component)
use key={phase.name}, which is not collision-resistant; change the key to a
unique identifier such as phase.id (or another stable unique property on the
phase object) and fall back to a deterministic combination if needed (e.g.,
`${phase.id ?? phase.name}-${i}`) so React reconciliation remains stable; update
the JSX where key is set (currently key={phase.name}) to use this unique
identifier.
apps/dashboard/src/components/case/__tests__/CaseTimeline.test.tsx-5-11 (1)

5-11: ⚠️ Potential issue | 🟡 Minor

Remove the unused motion mock and any type violations.

The CaseTimeline component uses Tailwind CSS animations (e.g., animate-ping) and doesn't import or use Framer Motion. The mock on lines 5–11 is unnecessary and introduces avoidable any types that violate TypeScript strictness.

Cleanup
-vi.mock('motion/react', () => ({
-  motion: {
-    div: ({ children, ...props }: any) => <div {...props}>{children}</div>,
-    span: ({ children, ...props }: any) => <span {...props}>{children}</span>,
-  },
-  AnimatePresence: ({ children }: any) => <>{children}</>,
-}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/__tests__/CaseTimeline.test.tsx` around
lines 5 - 11, Remove the unnecessary vi.mock('motion/react', ...) block from
CaseTimeline.test.tsx (the mocked motion.div, motion.span and AnimatePresence)
because CaseTimeline uses Tailwind animations not Framer Motion; delete the
entire mock and any lingering references to the mocked symbols (motion/react,
motion.div, motion.span, AnimatePresence) and replace any "any" type annotations
introduced there with proper typings or let TypeScript infer types to satisfy
strict mode in the test file.
apps/dashboard/src/components/case/__tests__/AnimatedEdge.test.tsx-32-32 (1)

32-32: ⚠️ Potential issue | 🟡 Minor

Remove the as any cast to improve type safety.

Line 32 suppresses type checking unnecessarily. Extract the component's prop type using Parameters<typeof AnimatedEdge>[0] and apply it to baseEdgeProps to let TypeScript validate the test setup properly.

Suggested approach
 import { AnimatedEdge } from '../AnimatedEdge';

+type AnimatedEdgeProps = Parameters<typeof AnimatedEdge>[0];
+
 describe('AnimatedEdge', () => {
-  const baseEdgeProps = {
+  const baseEdgeProps: AnimatedEdgeProps = {
     id: 'edge-1',
     source: 'node-1',
     target: 'node-2',
@@
-        <AnimatedEdge {...(baseEdgeProps as any)} />
+        <AnimatedEdge {...baseEdgeProps} />
       </svg>,
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/__tests__/AnimatedEdge.test.tsx` at line
32, Remove the `as any` cast on the AnimatedEdge render and give baseEdgeProps
the proper prop type so TypeScript can validate the test; import or reference
the component type by using Parameters<typeof AnimatedEdge>[0] as the type for
baseEdgeProps (replace its current declaration), then render <AnimatedEdge
{...baseEdgeProps} /> without the `as any` cast so the compiler enforces correct
props for AnimatedEdge.
apps/dashboard/src/components/case/__tests__/CaseGraph.test.tsx-5-27 (1)

5-27: ⚠️ Potential issue | 🟡 Minor

Replace any in mocks to resolve ESLint warnings and improve type safety.

Lines 6, 11, 16, 17, 25, and 27 use any, triggering ESLint warnings per the @typescript-eslint/no-explicit-any rule. Typing these mocks explicitly improves type safety without affecting test behavior.

Suggested typed mock shape
+import type { ReactNode, ComponentPropsWithoutRef } from 'react';

+type FlowMockProps = {
+  nodes: Array<unknown>;
+  edges: Array<unknown>;
+  children?: ReactNode;
+};

 vi.mock('@xyflow/react', () => ({
-  ReactFlow: ({ nodes, edges, children }: any) => (
+  ReactFlow: ({ nodes, edges, children }: FlowMockProps) => (
     <div data-testid="react-flow" data-nodes={nodes.length} data-edges={edges.length}>
       {children}
     </div>
   ),
-  ReactFlowProvider: ({ children }: any) => <>{children}</>,
+  ReactFlowProvider: ({ children }: { children?: ReactNode }) => <>{children}</>,
   Background: () => null,
   Controls: () => null,
   Handle: () => null,
   Position: { Top: 'top', Bottom: 'bottom', Left: 'left', Right: 'right' },
-  useNodesState: (init: any) => [init, vi.fn(), vi.fn()],
-  useEdgesState: (init: any) => [init, vi.fn(), vi.fn()],
+  useNodesState: <T,>(init: T[]) => [init, vi.fn(), vi.fn()],
+  useEdgesState: <T,>(init: T[]) => [init, vi.fn(), vi.fn()],
   getSmoothStepPath: () => ['M 0 0', 0, 0],
   BaseEdge: () => null,
 }));

 vi.mock('motion/react', () => ({
   motion: {
-    div: ({ children, ...props }: any) => <div {...props}>{children}</div>,
+    div: ({ children, ...props }: ComponentPropsWithoutRef<'div'>) => (
+      <div {...props}>{children}</div>
+    ),
   },
-  AnimatePresence: ({ children }: any) => <>{children}</>,
+  AnimatePresence: ({ children }: { children?: ReactNode }) => <>{children}</>,
 }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/__tests__/CaseGraph.test.tsx` around lines
5 - 27, Replace explicit any in the mocks by importing and using the real types
from the libraries and React: type the ReactFlow mock props as
React.PropsWithChildren<{ nodes: Node[]; edges: Edge[] }> (or the library's
ReactFlowProps) and mark the component as React.FC; type ReactFlowProvider and
AnimatePresence props as React.PropsWithChildren<unknown> or React.FC; type
Background, Controls, Handle, and BaseEdge as React.FC or the specific component
prop types from '@xyflow/react'; type Position as the library's Position type;
type useNodesState/useEdgesState to return [Node[],
React.Dispatch<React.SetStateAction<Node[]>>, unknown] and similarly for edges
using the Node/Edge types from '@xyflow/react'; and type motion.div props as
React.ComponentType<React.PropsWithChildren<MotionProps>> with children:
React.ReactNode; this removes any by referencing the library types (Node, Edge,
MotionProps, ReactFlowProps) when declaring the mock implementations like
ReactFlow, useNodesState, useEdgesState, motion.div, and AnimatePresence.
apps/dashboard/src/components/case/__tests__/CustomNodes.test.tsx-15-27 (1)

15-27: ⚠️ Potential issue | 🟡 Minor

Fix the any type to resolve the pipeline warning.

The pipeline flags Unexpected any at line 26. Since you're mocking React Flow's NodeProps, you can type this more precisely.

🔧 Proposed fix
+import type { NodeProps } from '@xyflow/react';
+
 // Helper to build mock node props
-function mockNodeProps<T extends Record<string, unknown>>(data: T) {
+function mockNodeProps<T extends Record<string, unknown>>(data: T): NodeProps<T> {
   return {
     id: 'test-node',
     data,
     type: 'custom',
     selected: false,
     isConnectable: true,
     zIndex: 0,
     positionAbsoluteX: 0,
     positionAbsoluteY: 0,
-  } as any;
+  } as NodeProps<T>;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/__tests__/CustomNodes.test.tsx` around
lines 15 - 27, The test helper mockNodeProps currently uses an unsafe any cast
causing the pipeline "Unexpected any" warning; replace the any with the proper
React Flow type by importing NodeProps from 'reactflow' and change the return
cast to NodeProps<T> (i.e., ensure mockNodeProps<T> returns NodeProps<T> and
import { NodeProps } from 'reactflow'), keeping the same object shape but
removing the any cast.
apps/dashboard/src/components/fleet/FleetCard.tsx-28-35 (1)

28-35: ⚠️ Potential issue | 🟡 Minor

Edge case: getInitials may produce undefined characters.

If name contains empty parts (e.g., double spaces or leading/trailing spaces), part[0] returns undefined, which .join('') converts to the string "undefined".

🛡️ Defensive fix
 function getInitials(name: string): string {
   return name
     .split(' ')
+    .filter(Boolean)
     .map((part) => part[0])
     .join('')
     .toUpperCase()
     .slice(0, 2);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/FleetCard.tsx` around lines 28 - 35, The
getInitials function can produce "undefined" when name has empty segments;
update getInitials to first trim the input, split on spaces, filter out empty
parts, and then safely take the first character of each part (e.g., via
part.charAt(0) or checking part.length) before joining, uppercasing and slicing;
reference the getInitials(name: string) function and ensure empty parts are
removed so only valid characters contribute to the result.
apps/dashboard/src/routes/__tests__/case.test.tsx-27-40 (1)

27-40: ⚠️ Potential issue | 🟡 Minor

Continue fixing any in remaining mocks.

🔧 Proposed fix for motion and router mocks
 vi.mock('motion/react', () => ({
   motion: {
-    div: ({ children, ...props }: any) => <div {...props}>{children}</div>,
-    span: ({ children, ...props }: any) => <span {...props}>{children}</span>,
+    div: ({ children, ...props }: React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode }) => <div {...props}>{children}</div>,
+    span: ({ children, ...props }: React.HTMLAttributes<HTMLSpanElement> & { children?: React.ReactNode }) => <span {...props}>{children}</span>,
   },
-  AnimatePresence: ({ children }: any) => <>{children}</>,
+  AnimatePresence: ({ children }: { children?: React.ReactNode }) => <>{children}</>,
 }));

 vi.mock('@tanstack/react-router', () => ({
   createFileRoute: () => () => ({}),
-  Link: ({ children, ...props }: any) => <a {...props}>{children}</a>,
+  Link: ({ children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & { children?: React.ReactNode }) => <a {...props}>{children}</a>,
   useNavigate: () => vi.fn(),
 }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/__tests__/case.test.tsx` around lines 27 - 40, The
mocks use untyped "any" props; update the mocked components in this test so
their props are properly typed instead of any: for motion.div and motion.span
use React.HTMLAttributes<HTMLDivElement> / React.HTMLAttributes<HTMLSpanElement>
(with children?: React.ReactNode) or React.FC<PropsWithChildren<...>> to match
real element props, type AnimatePresence as React.FC<{children?:
React.ReactNode}>, type Link props as
React.AnchorHTMLAttributes<HTMLAnchorElement> (children?: React.ReactNode), and
type useNavigate to return the correct navigate signature (e.g., (to: string |
{to: string}, opts?: any) => void) while leaving createFileRoute typed as a
function; update the vi.mock declarations for motion/react and
`@tanstack/react-router` to use these concrete React types for motion.div,
motion.span, AnimatePresence, Link, and the useNavigate mock.
apps/dashboard/src/routes/__tests__/case.test.tsx-7-21 (1)

7-21: ⚠️ Potential issue | 🟡 Minor

Pipeline warnings: Replace any with minimal types in mocks.

The CI is flagging multiple any usages. For test mocks, lightweight inline types suffice:

🔧 Proposed fix for mock typing
-vi.mock('@xyflow/react', () => ({
-  ReactFlow: ({ nodes, edges, children }: any) => (
+vi.mock('@xyflow/react', () => ({
+  ReactFlow: ({ nodes, edges, children }: { nodes: unknown[]; edges: unknown[]; children?: React.ReactNode }) => (
     <div data-testid="react-flow" data-nodes={nodes.length} data-edges={edges.length}>
       {children}
     </div>
   ),
-  ReactFlowProvider: ({ children }: any) => <>{children}</>,
+  ReactFlowProvider: ({ children }: { children?: React.ReactNode }) => <>{children}</>,
   Background: () => null,
   Controls: () => null,
   Handle: () => null,
   Position: { Top: 'top', Bottom: 'bottom', Left: 'left', Right: 'right' },
-  useNodesState: (init: any) => [init, vi.fn(), vi.fn()],
-  useEdgesState: (init: any) => [init, vi.fn(), vi.fn()],
+  useNodesState: <T,>(init: T) => [init, vi.fn(), vi.fn()],
+  useEdgesState: <T,>(init: T) => [init, vi.fn(), vi.fn()],
   getSmoothStepPath: () => ['M 0 0', 0, 0],
   BaseEdge: () => null,
 }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/__tests__/case.test.tsx` around lines 7 - 21,
Replace the broad any types in the React Flow test mocks with minimal, specific
types: use React.ReactNode for children in ReactFlow and ReactFlowProvider, type
the ReactFlow mock props as { nodes: Array<any>; edges: Array<any>; children?:
React.ReactNode } (or tighter element/node types if available) for ReactFlow,
type Position as the existing string literals, and change
useNodesState/useEdgesState signatures from (init: any) => [...] to (init:
Array<any> | unknown[]) => [typeof init, jest.Mock, jest.Mock] (or use generic
<T> if available) so the mocks (ReactFlow, ReactFlowProvider, useNodesState,
useEdgesState, Background, Controls, Handle, getSmoothStepPath, BaseEdge,
Position) avoid any while remaining lightweight for tests.
orchestration/AuthScript.AppHost/infra/gateway.tmpl.yaml-68-69 (1)

68-69: ⚠️ Potential issue | 🟡 Minor

Fix HTTP_PORTS port default to match configured binding.

Line 69 uses targetPortOrDefault 0 which produces an invalid port 0 for the HTTP_PORTS environment variable. The gateway's http binding is configured on port 8000 (per manifest.json), so this should be targetPortOrDefault 8000 to align with the actual binding and the secondary port 8001 (resulting in 8000;8001).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/gateway.tmpl.yaml` around lines 68 -
69, The HTTP_PORTS env value uses targetPortOrDefault 0 which yields an invalid
port 0; update the template in gateway.tmpl.yaml so the call targetPortOrDefault
uses 8000 (matching the gateway http binding in manifest.json) so HTTP_PORTS
becomes "8000;8001" instead of "0;8001"; change the targetPortOrDefault
invocation where HTTP_PORTS is set to reference 8000 and leave the secondary
port 8001 unchanged.
apps/dashboard/src/components/demo/DemoProvider.tsx-25-25 (1)

25-25: ⚠️ Potential issue | 🟡 Minor

Move context to a separate file to fix Fast Refresh warning.

The pipeline warning indicates that exporting DemoContext alongside the DemoProvider component breaks React Fast Refresh. Extract the context creation to a dedicated file.

Proposed structure

Create apps/dashboard/src/components/demo/DemoContext.ts:

import { createContext } from 'react';
import type { DemoContextValue } from './types';

export const DemoContext = createContext<DemoContextValue | null>(null);

Then import it in DemoProvider.tsx:

-export const DemoContext = createContext<DemoContextValue | null>(null);
+import { DemoContext } from './DemoContext';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/demo/DemoProvider.tsx` at line 25, Extract the
context creation out of the DemoProvider module by moving the createContext call
that defines DemoContext into its own module (e.g., a new DemoContext.ts) that
exports DemoContext (typed with DemoContextValue), then update the DemoProvider
to import DemoContext from that new module instead of exporting it alongside the
component; ensure the exported symbol names remain DemoContext and DemoProvider
and that DemoContextValue's type import is preserved where needed.
apps/dashboard/src/routes/ehr-demo.tsx-269-276 (1)

269-276: ⚠️ Potential issue | 🟡 Minor

"View in Fleet" button lacks onClick handler.

The button renders but has no action wired. If this is intentional scaffolding for future integration (per the plan's task-015/016), consider adding a TODO comment. Otherwise, wire it to the DemoContext's setScene('fleet').

Suggested fix if navigation is intended
+import { useContext } from 'react';
+import { DemoContext } from '@/components/demo/DemoProvider';

// Inside EhrDemoPage:
+const demoCtx = useContext(DemoContext);

 <button
   type="button"
+  onClick={() => demoCtx?.setScene('fleet')}
   className="inline-flex items-center gap-2 rounded-lg bg-teal-600 px-5 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-teal-700"
 >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/ehr-demo.tsx` around lines 269 - 276, The "View in
Fleet" button in ehr-demo.tsx currently has no onClick handler; either wire it
to the DemoContext by importing/using DemoContext (or useDemoContext hook) and
call setScene('fleet') inside the button's onClick, or if this is intentional
scaffolding add a clear TODO comment on the button explaining it's awaiting
task-015/016 integration; locate the button element (the JSX button with
className "inline-flex items-center gap-2 ..." and the ArrowRight icon) and
update it accordingly to invoke DemoContext.setScene('fleet') or include the
TODO.
docs/designs/2026-03-15-deploy-ehr-demo.md-20-33 (1)

20-33: ⚠️ Potential issue | 🟡 Minor

Label the ASCII diagram fence.

This block is the source of the current MD040 warning. Give it an explicit language such as text so the design doc stays lint-clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/designs/2026-03-15-deploy-ehr-demo.md` around lines 20 - 33, The fenced
ASCII diagram currently has an unlabeled code block causing MD040 lint warnings;
update the triple-backtick fence that wraps the ASCII diagram so it includes an
explicit language label (e.g., change ``` to ```text) to silence the MD040 rule
and keep the design doc lint-clean; locate the diagram block in the
deploy-ehr-demo design doc (the triple-backtick block containing "Internet" and
the ASCII arrows) and add the language label to the opening fence.
orchestration/AuthScript.AppHost/infra/resources.bicep-48-58 (1)

48-58: ⚠️ Potential issue | 🟡 Minor

Apply the shared tags to the storage account too.

tags is documented as applying to all resources, but storageVolume is the one top-level resource here that doesn't set it. That will break tag-based cost allocation and can trip tag-enforcement policies on the file-share backing store.

🏷️ Minimal fix
 resource storageVolume 'Microsoft.Storage/storageAccounts@2022-05-01' = {
   name: 'vol${resourceToken}'
   location: location
+  tags: tags
   kind: 'StorageV2'
   sku: {
     name: 'Standard_LRS'
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/resources.bicep` around lines 48 - 58,
The storage account resource 'storageVolume' is missing the shared tags, so add
the shared tags property to its declaration: include a tags: tags entry inside
the resource block for storageVolume (matching the project's shared tags
variable/name used elsewhere) so the storageVolume inherits the same tag set as
other resources and satisfies tag-based cost allocation and enforcement
policies.
docs/designs/2026-03-15-deploy-ehr-demo.md-153-165 (1)

153-165: ⚠️ Potential issue | 🟡 Minor

Clarify the layer: the .WithEnvironment() call belongs in AppHost, not Gateway Program.cs.

The code snippet shows AppHost resource wiring (WithEnvironment) but claims it's for "Gateway Program.cs". The Gateway actually reads CORS origins from configuration via the AddCorsPolicy() extension method (which uses services.AddCors() and .WithOrigins()). The environment variable should be set in orchestration/AuthScript.AppHost/AppHost.cs on the Gateway resource, not pasted into the Gateway's Program.cs. Clarify which file (AppHost or Gateway Program.cs) is being configured and align the code syntax with the actual implementation layer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/designs/2026-03-15-deploy-ehr-demo.md` around lines 153 - 165, The
snippet incorrectly places the AppHost-specific
.WithEnvironment("AllowedOrigins", ...) in "Gateway Program.cs"; move the
environment-variable wiring to the AppHost resource
(orchestration/AuthScript.AppHost/AppHost.cs) where AppHost is created and
configured, and update the docs to state that the Gateway reads CORS from
configuration via AddCorsPolicy (which uses services.AddCors() and
.WithOrigins()), so set the AllowedOrigins env var on AppHost (use AppHost's
.WithEnvironment) and ensure Gateway Program.cs simply relies on configuration
loaded by AddCorsPolicy/AddCors.
apps/dashboard/src/components/ehr/useEhrDemoFlow.ts-83-86 (1)

83-86: ⚠️ Potential issue | 🟡 Minor

Remove ref mutation during render and use callback dependency instead.

Lines 85–86 mutate onTransitionRef.current during the render phase, which violates React's rules. Instead, call options?.onTransition directly in transitionToFleet with it as a dependency:

Fix
 export function useEhrDemoFlow(options?: EhrDemoFlowOptions): EhrDemoFlow {
   const startState = options?.startState ?? 'idle';
-  const onTransitionRef = useRef(options?.onTransition);
-  onTransitionRef.current = options?.onTransition;
 
   const [state, setState] = useState<EhrDemoState>(startState);
@@
   const transitionToFleet = useCallback(() => {
-    onTransitionRef.current?.();
-  }, []);
+    options?.onTransition?.();
+  }, [options?.onTransition]);

This also applies to lines 214–216.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/ehr/useEhrDemoFlow.ts` around lines 83 - 86,
The code mutates onTransitionRef.current during render in useEhrDemoFlow; remove
that ref mutation and instead reference options?.onTransition directly (or
capture it via a useCallback/useEffect dependency) where transitions occur —
specifically update transitionToFleet (and the other transition functions around
the area corresponding to lines 214–216) to call options?.onTransition passed in
as a dependency or parameter so you don't assign to onTransitionRef during
render; ensure any callback created with useCallback lists options?.onTransition
in its dependency array.
apps/dashboard/src/routes/fleet.tsx-6-6 (1)

6-6: ⚠️ Potential issue | 🟡 Minor

Remove the unused usePAStats() hook and its import.

The hook is called at line 82 but the result is discarded (assigned to _graphqlStats with a leading underscore), triggering linter warnings and making an unnecessary GraphQL request.

✂️ Cleanup
-import { usePARequests, usePAStats, useActivity, type ActivityItem } from '@/api/graphqlService';
+import { usePARequests, useActivity, type ActivityItem } from '@/api/graphqlService';
-  const { data: _graphqlStats } = usePAStats();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/fleet.tsx` at line 6, The code calls usePAStats()
and assigns its result to _graphqlStats but never uses it; remove the unused
hook call and its import to stop the extra GraphQL request and linter warnings.
Delete usePAStats from the import list (where usePARequests, useActivity, type
ActivityItem are imported) and remove the line that calls usePAStats() (the
assignment to _graphqlStats), ensuring no other references to _graphqlStats
remain.
🧹 Nitpick comments (15)
apps/gateway/Gateway.API.Tests/Data/SeedDemoDataTests.cs (1)

94-107: This still doesn't prove repeatability.

The test on Lines 98-106 only checks that the generated timestamps span roughly a week. It will still pass if two invocations produce different CreatedAt, UpdatedAt, SubmittedAt, or ServiceDate values. Once the live clock is removed from SeedDemoData.GenerateSeedRequests(), add a second-call comparison so the determinism contract is actually covered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/Gateway.API.Tests/Data/SeedDemoDataTests.cs` around lines 94 -
107, The test currently only checks timestamp span but not determinism; after
removing any live-clock usage in SeedDemoData.GenerateSeedRequests(), call
GenerateSeedRequests() a second time within
SeedDemoData_TimestampsSpreadOverWeek and assert that the two result sets are
identical for the timestamp fields (CreatedAt, UpdatedAt, SubmittedAt,
ServiceDate) — e.g., compare the sequences element-by-element or by projecting
those fields and asserting sequence equality (use the existing requests variable
and a new requests2 from GenerateSeedRequests()) to ensure repeatability.
apps/gateway/Gateway.API/Data/SeedDemoData.cs (1)

104-109: Keep the status rules in one place.

status drives confidence, criteria selection, ReadyAt, and SubmittedAt in separate branches here. The Template 4 comment on Lines 104-109 already diverges from the mapping on Line 159, which is a sign these rules are starting to drift. A small per-status config/strategy would make future changes much safer.

As per coding guidelines, "SOLID: One public type per file, Strategy over switches, constructor injection".

Also applies to: 149-170

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/Gateway.API/Data/SeedDemoData.cs` around lines 104 - 109,
Centralize the per-status rules used during demo data seeding (the logic that
sets status, criteria selection, ReadyAt, SubmittedAt, and confidence) into a
single strategy/config object instead of duplicating branches; create a small
internal type (e.g., DemoStatusConfig or ClaimStatusStrategy) that maps each
status to its criteria template, confidence, ReadyAt/SubmittedAt behavior and
use that map from the seeding code (replace the separate branches that build
Template 4 and the later status mapping logic). Update the seeder to look up the
config for a given status and apply the unified rules so all status-related
decisions live in one place; keep the new type internal to this file (or a
single new file if public is required) and wire it into the existing seeding
methods that currently reference status, criteria selection, ReadyAt and
SubmittedAt.
apps/dashboard/src/components/case/EvidenceNode.tsx (1)

25-25: Define a named props interface for EvidenceNode instead of inline intersection typing.

This keeps the component aligned with the project TSX props standard and improves consistency with adjacent components.

♻️ Proposed refactor
 export interface EvidenceNodeData {
   text: string;
   source: string;
 }

+interface EvidenceNodeProps extends NodeProps {
+  data: EvidenceNodeData;
+}
+
 /**
  * Custom React Flow node displaying a piece of clinical evidence.
  * Layout: evidence text + small source badge (colored by type).
  */
-export function EvidenceNode({ data }: NodeProps & { data: EvidenceNodeData }) {
+export function EvidenceNode({ data }: EvidenceNodeProps) {
   const sourceColor =
     SOURCE_COLORS[data.source] ?? 'bg-slate-100 text-slate-600';

As per coding guidelines, apps/dashboard/**/*.tsx: "Props: Define interface for props, use destructuring".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/EvidenceNode.tsx` at line 25, The
EvidenceNode component currently uses an inline intersection type for its props
(NodeProps & { data: EvidenceNodeData }); replace this with a named interface
(e.g., interface EvidenceNodeProps extends NodeProps { data: EvidenceNodeData })
and update the component signature to use destructured props: function
EvidenceNode({ data }: EvidenceNodeProps) so it follows the project's TSX props
standard and matches adjacent components; ensure to export or keep visibility
consistent with the original component.
apps/dashboard/src/components/fleet/CasePipeline.tsx (1)

74-105: Consider adding accessibility attributes to stage buttons.

The pipeline buttons control filtering state but lack aria-pressed or aria-label attributes. Screen reader users would benefit from knowing the current active state.

Accessibility improvement
           <button
             key={stage.key}
             data-testid={`pipeline-stage-${stage.key}`}
             onClick={() => onFilter(stage.key)}
+            aria-pressed={isActive}
+            aria-label={`${stage.label}: ${count} cases`}
             className={cn(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/CasePipeline.tsx` around lines 74 - 105,
Add ARIA attributes to the pipeline stage button: set aria-pressed to the
boolean isActive and add a descriptive aria-label (e.g., `Filter by
{stage.label}, {count} cases` or similar) so screen readers announce the control
and its active state; update the button rendered in CasePipeline (the <button
key={stage.key} ...> element with data-testid={`pipeline-stage-${stage.key}`})
to include these attributes while keeping existing props and handlers (onClick,
data-testid, className) intact.
apps/dashboard/src/components/case/PatientNode.tsx (1)

17-22: Initials computation assumes non-empty name parts.

w[0] will be undefined if a name contains consecutive spaces or is empty. For demo purposes with controlled data this is fine, but production code would benefit from filtering empty parts:

const initials = data.name
  .split(' ')
  .filter(Boolean)
  .map((w) => w[0])
  .join('')
  .toUpperCase()
  .slice(0, 2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/PatientNode.tsx` around lines 17 - 22, The
initials computation in PatientNode (const initials) assumes non-empty name
parts and can produce undefined when data.name has consecutive spaces; update
the logic that builds initials to filter out empty segments before mapping
(i.e., call .split(' ') then .filter(Boolean) on the resulting array) so
.map((w) => w[0]) only runs on non-empty words, then continue with
.join('').toUpperCase().slice(0, 2) using data.name as the source.
apps/dashboard/src/components/fleet/FleetView.tsx (1)

4-19: Tighten filter typing to the request status union.

Line 6 uses string | null, which permits invalid status values. Prefer typing filter from FleetPARequest['status'] for stronger compile-time safety.

Suggested typing refinement
 interface FleetViewProps {
   requests: FleetPARequest[];
-  filter: string | null;
+  filter: FleetPARequest['status'] | null;
   highlightedCaseId?: string;
   onSelectCase: (id: string) => void;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/FleetView.tsx` around lines 4 - 19, The
filter prop is currently typed as string | null which allows invalid statuses;
update FleetViewProps so filter is typed as FleetPARequest['status'] | null and
adjust the FleetView parameter type accordingly so the existing visible
calculation (const visible = filter ? requests.filter((r) => r.status ===
filter) : requests) remains type-safe; ensure any callers pass the narrowed
union type or cast accordingly to satisfy the new FleetViewProps signature.
apps/dashboard/src/components/demo/__tests__/DemoProvider.test.tsx (1)

8-21: Refactor renderWithProvider to use a cleaner testing pattern.

Consider using renderHook with a wrapper component instead of manually managing context capture. This approach is more idiomatic for testing hooks and avoids render-time side effects.

Suggested refactor
-import { render, screen, act } from '@testing-library/react';
-import { createElement, useContext } from 'react';
+import { renderHook, act } from '@testing-library/react';
+import { useContext, type PropsWithChildren } from 'react';

 function renderWithProvider(providerProps?: { autoPlay?: boolean }) {
-  let contextValue: DemoContextValue | null = null;
-
-  function Consumer() {
-    contextValue = useContext(DemoContext);
-    if (!contextValue) throw new Error('DemoContext not found');
-    return createElement('div', { 'data-testid': 'scene' }, contextValue.scene);
-  }
-
-  const result = render(
-    createElement(DemoProvider, { ...providerProps, children: createElement(Consumer) }),
-  );
-
-  return { result, getContext: () => contextValue! };
+  const Wrapper = ({ children }: PropsWithChildren) => (
+    <DemoProvider {...providerProps}>{children}</DemoProvider>
+  );
+
+  const { result } = renderHook(() => {
+    const ctx = useContext(DemoContext);
+    if (!ctx) throw new Error('DemoContext not found');
+    return ctx;
+  }, { wrapper: Wrapper });
+
+  return { getContext: () => result.current };
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/demo/__tests__/DemoProvider.test.tsx` around
lines 8 - 21, The test helper renderWithProvider manually mounts a Consumer
component to capture DemoContext via useContext (functions: renderWithProvider,
Consumer, getContext, DemoProvider, DemoContextValue), which is fragile;
refactor to use renderHook from testing-library/react with a wrapper that
returns <DemoProvider {...providerProps}>{children}</DemoProvider> so you can
call renderHook(() => useContext(DemoContext), { wrapper }) and access the typed
context value directly, removing the manual Consumer and data-testid element and
simplifying getContext to return the hook's result.current.
apps/dashboard/src/components/ehr/ChartTabPanel.tsx (1)

5-8: Consider narrowing activeTab to a union type.

Using string for activeTab is flexible but loses type safety. A union type would catch invalid tab values at compile time.

♻️ Suggested improvement
+type ChartTab = 'problems' | 'medications' | 'allergies' | 'imaging' | 'vitals' | 'labs';
+
 interface ChartTabPanelProps {
-  activeTab: string;
+  activeTab: ChartTab;
   chartData: ChartData;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/ehr/ChartTabPanel.tsx` around lines 5 - 8, The
prop type ChartTabPanelProps uses activeTab: string which loses type safety;
define a TabKey union type (e.g., type TabKey = 'overview' | 'labs' | 'vitals' |
'meds' or whatever actual tab ids are used in this component set) and change
ChartTabPanelProps.activeTab to that union, update the ChartTabPanel component
and any callers to use the TabKey type so invalid tab values are caught at
compile time; also export or reuse the TabKey where other tab components/readers
expect the same keys to keep types consistent.
docs/plans/2026-03-15-deploy-ehr-demo.md (1)

128-136: Optional: Add language specifiers to code blocks.

Markdownlint flags lines 130 and 174 for missing language identifiers.

📝 Fix markdown lint warnings
 - Add Dashboard to `build:containers` script:
-  ```
+  ```bash
   docker build -f apps/dashboard/Dockerfile -t authscript-dashboard .

And for the dependency graph (line 174), use `text` or leave as-is since ASCII art doesn't benefit from highlighting:
```diff
-```
+```text
 Task 1 (GraphQL client) ──► Task 2 (Dockerfile) ──► Task 3 (AppHost)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/plans/2026-03-15-deploy-ehr-demo.md` around lines 128 - 136, Add
explicit language identifiers to the two markdown code fences mentioned in the
diff: mark the Docker build command block with "bash" (the one showing docker
build -f apps/dashboard/Dockerfile -t authscript-dashboard .) and mark the
dependency graph ASCII art block with "text" (or leave as-is if preferred), so
markdownlint warnings for missing language identifiers are resolved; update the
code fences surrounding the Docker command and the dependency graph accordingly
in the deployment plan document.
apps/dashboard/src/routes/__tests__/case.test.tsx (1)

42-47: Avoid as any for module access.

♻️ Type the dynamic import
 async function renderCaseDetailPage() {
   const mod = await import('../case.$caseId');
-  // The module exports CaseDetailPage as the component
-  const Component = (mod as any).CaseDetailPage;
+  const Component = (mod as { CaseDetailPage: React.ComponentType }).CaseDetailPage;
   return render(createElement(Component));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/__tests__/case.test.tsx` around lines 42 - 47,
Replace the unsafe "as any" cast in renderCaseDetailPage by typing the dynamic
import to the module shape: assert the imported module as the proper module type
(e.g., use "as typeof import('../case.$caseId')" or define an interface like "{
CaseDetailPage: ComponentType }" and cast the import to that) and then pull
CaseDetailPage from that typed module; update the renderCaseDetailPage function
to use the typed import instead of "(mod as any).CaseDetailPage" so TypeScript
knows the component type.
orchestration/AuthScript.AppHost/infra/dashboard.tmpl.yaml (1)

12-14: Remove .NET runtime configuration from static frontend.

The dashboard is an nginx-based static frontend container. The dotnet.autoConfigureDataProtection setting is irrelevant and can be removed.

Proposed fix
   configuration:
     activeRevisionsMode: single
-    runtime:
-      dotnet:
-        autoConfigureDataProtection: true
     ingress:
       external: true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/dashboard.tmpl.yaml` around lines 12 -
14, Remove the irrelevant .NET runtime configuration from the static nginx
frontend template by deleting the runtime: dotnet: autoConfigureDataProtection
entry in dashboard.tmpl.yaml; specifically remove the
autoConfigureDataProtection key and its parent dotnet/runtime stanza so the
static frontend YAML no longer contains .NET-specific settings and remains valid
YAML.
orchestration/AuthScript.AppHost/infra/postgres.tmpl.yaml (1)

12-14: Remove irrelevant .NET runtime configuration.

The dotnet.autoConfigureDataProtection setting is unnecessary for a Postgres container image — it only applies to ASP.NET Core applications. This appears to be copy-paste from other service templates.

Proposed fix
   configuration:
     activeRevisionsMode: single
-    runtime:
-      dotnet:
-        autoConfigureDataProtection: true
     ingress:
       external: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/postgres.tmpl.yaml` around lines 12 -
14, Remove the irrelevant .NET runtime configuration by deleting the
runtime.dotnet.autoConfigureDataProtection entry from the template; if the
runtime.dotnet block becomes empty after removal, delete the entire
runtime.dotnet (or runtime) block to avoid leaving an empty section in the
Postgres container template (target the runtime and dotnet keys shown in the
diff).
docs/plans/2026-03-15-pa-dashboard-demo.md (1)

9-28: Add language specifiers to fenced code blocks.

The ASCII dependency diagrams should specify a language (e.g., text or plaintext) to satisfy markdownlint and improve accessibility.

Proposed fix
-```
+```text
 Group 1 (Foundation) ─── sequential

Apply similarly to the diagram at line 22 and the parallelization summary at line 561.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/plans/2026-03-15-pa-dashboard-demo.md` around lines 9 - 28, The fenced
ASCII diagrams currently use unmarked code fences; update each relevant fence to
include a language specifier (e.g., change ``` to ```text or ```plaintext) for
the block that begins with "Group 1 (Foundation) ─── sequential" and the block
that begins with "task-001 ──► task-002 ──► task-003 ──┬──► ..." as well as the
parallelization summary block referenced in the review, so markdownlint passes
and screen readers treat them as plain text.
apps/dashboard/src/components/case/CaseGraph.tsx (1)

36-39: Move buildCaseGraphData into a sibling .ts module.

Line 36 is what triggers the current Fast Refresh warning: this .tsx file now exports non-component helpers alongside CaseGraph. Pull the graph builder/status helpers into a separate module so tests can still import them without disabling React refresh for the component file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/CaseGraph.tsx` around lines 36 - 39,
buildCaseGraphData is a non-component helper exported from the CaseGraph .tsx
which triggers Fast Refresh warnings; move that function (and any related
helpers for graph building/status) into a new sibling .ts module, export it
there using the same signature (buildCaseGraphData(paRequest: PARequest): {
nodes: Node[]; edges: Edge[] }), then update CaseGraph to import
buildCaseGraphData (and any moved helpers) from the new module and update tests
to import the helper from the new .ts file so the .tsx file only exports React
components.
orchestration/AuthScript.AppHost/infra/resources.bicep (1)

18-25: Verify whether the registry really needs the default public posture.

This ACR is only being introduced for internal build/pull flows, but the template leaves networking/scanning/quarantine at defaults. If you're not planning private networking yet, at least confirm the team is comfortable with a publicly reachable Basic registry for the demo environment.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/resources.bicep` around lines 18 - 25,
The ACR resource (resource containerRegistry with name
replace('acr-${resourceToken}', '-', '') and sku 'Basic') is left with default
public networking and policy posture; confirm with the team whether a publicly
reachable Basic registry is acceptable for the demo, and if not update the
template to lock it down: replace or augment the containerRegistry definition to
enforce private access (add networkRuleSet / private endpoints or disable public
network access), enable appropriate image scanning/quarantine policies under
policies (quarantinePolicy/vulnerabilityAssessment) and/or bump sku if required
for private endpoints, or add a short comment documenting the team's acceptance
of the current public defaults.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8f7da362-b318-420c-81b4-e68da7a36d0f

📥 Commits

Reviewing files that changed from the base of the PR and between bd95a82 and a8a2aa7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (77)
  • apps/dashboard/Dockerfile
  • apps/dashboard/nginx.conf
  • apps/dashboard/package.json
  • apps/dashboard/src/api/graphqlClient.ts
  • apps/dashboard/src/components/case/AnimatedEdge.tsx
  • apps/dashboard/src/components/case/CaseGraph.tsx
  • apps/dashboard/src/components/case/CaseTimeline.tsx
  • apps/dashboard/src/components/case/CriteriaNode.tsx
  • apps/dashboard/src/components/case/DecisionNode.tsx
  • apps/dashboard/src/components/case/EvidenceNode.tsx
  • apps/dashboard/src/components/case/NodeCard.tsx
  • apps/dashboard/src/components/case/PatientNode.tsx
  • apps/dashboard/src/components/case/__tests__/AnimatedEdge.test.tsx
  • apps/dashboard/src/components/case/__tests__/CaseGraph.test.tsx
  • apps/dashboard/src/components/case/__tests__/CaseTimeline.test.tsx
  • apps/dashboard/src/components/case/__tests__/CustomNodes.test.tsx
  • apps/dashboard/src/components/demo/DemoProvider.tsx
  • apps/dashboard/src/components/demo/SceneNav.tsx
  • apps/dashboard/src/components/demo/SceneTransition.tsx
  • apps/dashboard/src/components/demo/__tests__/DemoProvider.test.tsx
  • apps/dashboard/src/components/demo/__tests__/SceneNav.test.tsx
  • apps/dashboard/src/components/demo/__tests__/SceneTransition.test.tsx
  • apps/dashboard/src/components/demo/transitionVariants.ts
  • apps/dashboard/src/components/ehr/AuthDetectionBanner.tsx
  • apps/dashboard/src/components/ehr/ChartTabPanel.tsx
  • apps/dashboard/src/components/ehr/EhrHeader.tsx
  • apps/dashboard/src/components/ehr/EncounterSidebar.tsx
  • apps/dashboard/src/components/ehr/__tests__/AuthDetectionBanner.test.tsx
  • apps/dashboard/src/components/ehr/__tests__/ChartTabPanel.test.tsx
  • apps/dashboard/src/components/ehr/__tests__/EhrHeader.test.tsx
  • apps/dashboard/src/components/ehr/__tests__/EncounterSidebar.test.tsx
  • apps/dashboard/src/components/ehr/__tests__/useEhrDemoFlow.test.ts
  • apps/dashboard/src/components/ehr/useEhrDemoFlow.ts
  • apps/dashboard/src/components/fleet/CasePipeline.tsx
  • apps/dashboard/src/components/fleet/FleetCard.tsx
  • apps/dashboard/src/components/fleet/FleetView.tsx
  • apps/dashboard/src/components/fleet/KPICards.tsx
  • apps/dashboard/src/components/fleet/__tests__/CasePipeline.test.tsx
  • apps/dashboard/src/components/fleet/__tests__/FleetCard.test.tsx
  • apps/dashboard/src/components/fleet/__tests__/FleetView.test.tsx
  • apps/dashboard/src/components/fleet/__tests__/KPICards.test.tsx
  • apps/dashboard/src/lib/__tests__/demoData.test.ts
  • apps/dashboard/src/lib/__tests__/fleetSeedData.test.ts
  • apps/dashboard/src/lib/demoData.ts
  • apps/dashboard/src/lib/fleetSeedData.ts
  • apps/dashboard/src/routeTree.gen.ts
  • apps/dashboard/src/routes/__tests__/case.test.tsx
  • apps/dashboard/src/routes/__tests__/ehr-demo.test.tsx
  • apps/dashboard/src/routes/__tests__/fleet.test.tsx
  • apps/dashboard/src/routes/case.$caseId.tsx
  • apps/dashboard/src/routes/ehr-demo.tsx
  • apps/dashboard/src/routes/fleet.tsx
  • apps/gateway/Gateway.API.Tests/Data/SeedDemoDataTests.cs
  • apps/gateway/Gateway.API/Data/SeedDemoData.cs
  • docs/designs/2026-03-15-deploy-ehr-demo.md
  • docs/designs/2026-03-15-pa-dashboard-demo.md
  • docs/market-research/palantir-dashboard.md
  • docs/plans/2026-03-15-deploy-ehr-demo.md
  • docs/plans/2026-03-15-pa-dashboard-demo.md
  • orchestration/AuthScript.AppHost/.gitignore
  • orchestration/AuthScript.AppHost/AppHost.cs
  • orchestration/AuthScript.AppHost/AuthScript.AppHost.csproj
  • orchestration/AuthScript.AppHost/azure.yaml
  • orchestration/AuthScript.AppHost/infra/dashboard.tmpl.yaml
  • orchestration/AuthScript.AppHost/infra/dashboard/Dockerfile
  • orchestration/AuthScript.AppHost/infra/gateway.tmpl.yaml
  • orchestration/AuthScript.AppHost/infra/intelligence.tmpl.yaml
  • orchestration/AuthScript.AppHost/infra/intelligence/Dockerfile
  • orchestration/AuthScript.AppHost/infra/main.bicep
  • orchestration/AuthScript.AppHost/infra/main.parameters.json
  • orchestration/AuthScript.AppHost/infra/postgres.tmpl.yaml
  • orchestration/AuthScript.AppHost/infra/redis.tmpl.yaml
  • orchestration/AuthScript.AppHost/infra/resources.bicep
  • orchestration/AuthScript.AppHost/manifest.json
  • orchestration/AuthScript.AppHost/next-steps.md
  • package.json
  • scripts/azd-sync-secrets.sh

Comment on lines +23 to +34
FROM docker.io/nginx:alpine

# Copy built SPA assets
COPY --from=build /app/apps/dashboard/dist /usr/share/nginx/html

# Copy nginx config template (envsubst resolves $GATEWAY_URL at startup)
COPY apps/dashboard/nginx.conf /etc/nginx/templates/default.conf.template

EXPOSE 80

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -q --spider http://localhost/health || exit 1
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden runtime image to avoid running as root.

The final stage lacks a USER directive, and scanner DS-0002 flags this. Please switch to an unprivileged nginx image/user and align exposed/healthcheck port accordingly.

Suggested hardening baseline
-FROM docker.io/nginx:alpine
+FROM docker.io/nginxinc/nginx-unprivileged:stable-alpine

 # Copy built SPA assets
 COPY --from=build /app/apps/dashboard/dist /usr/share/nginx/html

 # Copy nginx config template (envsubst resolves $GATEWAY_URL at startup)
 COPY apps/dashboard/nginx.conf /etc/nginx/templates/default.conf.template

-EXPOSE 80
+EXPOSE 8080

 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
-    CMD wget -q --spider http://localhost/health || exit 1
+    CMD wget -q --spider http://localhost:8080/health || exit 1

Also ensure nginx is configured to listen on 8080 to match this change.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM docker.io/nginx:alpine
# Copy built SPA assets
COPY --from=build /app/apps/dashboard/dist /usr/share/nginx/html
# Copy nginx config template (envsubst resolves $GATEWAY_URL at startup)
COPY apps/dashboard/nginx.conf /etc/nginx/templates/default.conf.template
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -q --spider http://localhost/health || exit 1
FROM docker.io/nginxinc/nginx-unprivileged:stable-alpine
# Copy built SPA assets
COPY --from=build /app/apps/dashboard/dist /usr/share/nginx/html
# Copy nginx config template (envsubst resolves $GATEWAY_URL at startup)
COPY apps/dashboard/nginx.conf /etc/nginx/templates/default.conf.template
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -q --spider http://localhost:8080/health || exit 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/Dockerfile` around lines 23 - 34, The final Dockerfile stage
runs nginx as root and must be hardened by switching to an unprivileged runtime
user and adjusting ports; update the Dockerfile to run nginx under a non-root
user (e.g., USER nginx or create a low-privilege user and set appropriate
ownership on /usr/share/nginx/html), change EXPOSE 80 to EXPOSE 8080, and update
the HEALTHCHECK to probe http://localhost:8080/health; also update the nginx
config template referenced by COPY apps/dashboard/nginx.conf (ensure the server
listen directive uses 8080) so nginx actually listens on the new port.

Comment on lines 186 to +207
const reset = useCallback(() => {
cancelledRef.current = true;
setState('idle');
setState(startState);
setPaRequest(null);
setPreCheckCriteria(null);
setEncounter(DEMO_ENCOUNTER_BASE);
setDocState('idle');
setInsertedHpiText(null);
setError(null);
}, [startState]);

// --- Enhanced flow actions ---

const addOrder = useCallback(() => {
if (state !== 'chart-browsing') return;
setState('order-entry');

// Auto-transition to pa-detected after delay
setTimeout(() => {
setState('pa-detected');
}, ORDER_ENTRY_DELAY_MS);
}, [state]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

addOrder() can resurrect the flow after reset.

The delayed setState('pa-detected') is never cancelled. If the user resets during the 1.2s order-entry delay, the stale timeout will still fire and push the state back into pa-detected. Store the timer id and clear it from reset() and cleanup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/ehr/useEhrDemoFlow.ts` around lines 186 - 207,
The delayed transition in addOrder() can fire after reset; store the timer id in
a ref (e.g., orderTimerRef) when calling setTimeout(…, ORDER_ENTRY_DELAY_MS)
inside addOrder(), and in reset() call clearTimeout(orderTimerRef.current) and
set orderTimerRef.current = null; also clear that timer in the component cleanup
(useEffect return) to prevent any stale setState after unmount; optionally also
guard the timeout callback by checking cancelledRef.current before calling
setState('pa-detected') for extra safety.

Comment on lines +13 to +15
export const Route = createFileRoute('/case/$caseId')({
component: CaseDetailPage,
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine the file
find . -path "*/apps/dashboard/src/routes/case.$caseId.tsx" -type f

Repository: lvlup-sw/authscript

Length of output: 45


🏁 Script executed:

# Read the file to see full implementation
cat -n apps/dashboard/src/routes/case.\$caseId.tsx

Repository: lvlup-sw/authscript

Length of output: 11330


🏁 Script executed:

# Search for DEMO_PA_RESULT usage in the codebase
rg "DEMO_PA_RESULT" --type tsx --type ts

Repository: lvlup-sw/authscript

Length of output: 90


🏁 Script executed:

# Look for useParams or route parameter access in case detail files
rg "useParams|useRoute|\$caseId|getCaseId" apps/dashboard/src/routes/ --type tsx --type ts -A 3

Repository: lvlup-sw/authscript

Length of output: 90


The detail route ignores $caseId, rendering only demo data.

CaseDetailPage hardcodes const paRequest = DEMO_PA_RESULT (line 126), so /case/1 and /case/48 display identical data. Once fleet navigation is wired, users will see the same record regardless of which case they select.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/case`.$caseId.tsx around lines 13 - 15, The
CaseDetailPage is using a hardcoded const paRequest = DEMO_PA_RESULT so the
Route export (createFileRoute('/case/$caseId') with component: CaseDetailPage)
ignores the $caseId param; change the flow to read the route param and load real
data instead of demo data—either add a loader that reads params.caseId and
fetches the case (and return it to the component) or update CaseDetailPage to
call useParams() (or accept a prop) and fetch the record by caseId, replacing
DEMO_PA_RESULT with the fetched paRequest; ensure the loader/params key names
match the createFileRoute param ($caseId) and that CaseDetailPage consumes the
provided data.

Comment on lines +107 to +121
const handlePipelineFilter = (stage: string) => {
// Map pipeline stages to fleet filter statuses
const stageToStatus: Record<string, string | null> = {
order_signed: null,
pa_detected: null,
processing: 'processing',
ready: 'ready',
submitted: 'submitted',
payer_response: null,
};
const status = stageToStatus[stage];
if (status) {
setActiveFilter((prev) => (prev === status ? null : status));
}
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Half of the pipeline buttons are currently inert.

order_signed, pa_detected, and payer_response map to null, and handlePipelineFilter() only updates state when the mapped value is truthy. Those stages can never become active or change the fleet view, even though CasePipeline renders them as clickable filters. Keep pipeline-stage selection separate from the single-status filter, or derive grouped request subsets before passing data to FleetView.

Also applies to: 164-167

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/fleet.tsx` around lines 107 - 121, The handler
handlePipelineFilter currently maps several pipeline stages to null in
stageToStatus so stages like order_signed, pa_detected, and payer_response never
activate; change the logic to manage pipeline-stage selection separately from
the single-status filter by introducing/using a pipelineStage state (or toggle
behavior) instead of mapping to null, e.g., have handlePipelineFilter toggle
pipelineStage for any stage (and only setActiveFilter when a stage maps to a
real status like 'processing'|'ready'|'submitted'), and ensure CasePipeline
renders communicate the selected pipelineStage to FleetView (or pre-filter data
into FleetView) so those stages become selectable and affect the fleet view;
also apply the same fix to the analogous logic referenced around the other
occurrence (the block that mirrors lines 164-167).

Comment on lines +191 to +197
<FleetView
requests={fleetData}
filter={activeFilter}
onSelectCase={() => {
// Will be wired to case detail navigation in future task
}}
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Case selection never reaches scene 3.

FleetView exposes onSelectCase, but this handler is a no-op. That leaves the new /case/$caseId route unreachable from the grid, so the command-center scene cannot drill into the case-detail scene.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/fleet.tsx` around lines 191 - 197, The onSelectCase
prop passed to FleetView is a no-op, preventing navigation into the case detail
route; replace the empty handler in the FleetView call with a function that
accepts the selected case id and navigates to the case route (e.g., use
react-router's useNavigate or history.push to go to `/case/${caseId}`), so
update the FleetView invocation in fleet.tsx to call navigate(`/case/${caseId}`)
(or the project's routing helper) inside the handler to open the case-detail
scene.

Comment on lines +129 to +132
public static List<PARequestModel> GenerateSeedRequests()
{
var rng = new Random(42); // deterministic seed
var now = DateTimeOffset.UtcNow;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The generator still depends on wall clock and host culture.

Line 132 uses DateTimeOffset.UtcNow, and Line 180 formats ServiceDate with the process culture. Two runs on different days or different locales will therefore emit different payloads even though this helper is documented as deterministic. Please freeze a reference instant for demo data, or move the clock behind an injected dependency, and format ServiceDate with CultureInfo.InvariantCulture.

Also applies to: 180-180

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/Gateway.API/Data/SeedDemoData.cs` around lines 129 - 132,
GenerateSeedRequests currently uses DateTimeOffset.UtcNow and formats
PARequestModel.ServiceDate with the ambient culture, causing non-deterministic
outputs; change it to use a frozen reference instant (e.g., a fixed
DateTimeOffset constant like DemoDataEpoch) or accept an injected
IClock/DateTimeProvider into GenerateSeedRequests so the clock is controllable
for tests, and update any ServiceDate.ToString(...) calls to use
CultureInfo.InvariantCulture when serializing/formatting ServiceDate; ensure
Random remains seeded (Random(42)) so only the time/culture dependency is
removed.

Comment on lines +23 to +34
FROM docker.io/nginx:alpine

# Copy built SPA assets
COPY --from=build /app/apps/dashboard/dist /usr/share/nginx/html

# Copy nginx config template (envsubst resolves $GATEWAY_URL at startup)
COPY apps/dashboard/nginx.conf /etc/nginx/templates/default.conf.template

EXPOSE 80

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -q --spider http://localhost/health || exit 1
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify runtime user declaration and port consistency
rg -n '^\s*USER\b' orchestration/AuthScript.AppHost/infra/dashboard/Dockerfile
rg -n '^\s*EXPOSE\b' orchestration/AuthScript.AppHost/infra/dashboard/Dockerfile
rg -n '^\s*listen\s+' apps/dashboard/nginx.conf

Repository: lvlup-sw/authscript

Length of output: 92


🏁 Script executed:

cat apps/dashboard/nginx.conf

Repository: lvlup-sw/authscript

Length of output: 891


🏁 Script executed:

cat orchestration/AuthScript.AppHost/infra/dashboard/Dockerfile

Repository: lvlup-sw/authscript

Length of output: 1214


Add non-root USER to run nginx unprivileged.

The image runs as root by default (no USER directive), which is a security gap. The nginx Alpine image includes a built-in nginx user. Apply the suggested hardening:

  • Add USER nginx before EXPOSE
  • Change EXPOSE 80 to EXPOSE 8080 (non-root cannot bind ports < 1024)
  • Update apps/dashboard/nginx.conf line 2: change listen 80; to listen 8080;
  • Update HEALTHCHECK to query port 8080: http://localhost:8080/health
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/dashboard/Dockerfile` around lines 23
- 34, Run the nginx process as the non-root nginx user and switch the container
to a non-privileged port: add a USER nginx directive in the Dockerfile before
EXPOSE, change EXPOSE 80 to EXPOSE 8080, and update the HEALTHCHECK in the
Dockerfile to hit http://localhost:8080/health; also update the nginx config
file apps/dashboard/nginx.conf (the listen directive on line 2) to use listen
8080; ensure these three changes are applied together so the container runs
unprivileged and nginx binds to 8080.

Comment on lines +15 to +45
@secure()
param athena_client_id string
@secure()
param athena_client_secret string
param athena_practice_id string
param azure_openai_endpoint string
@secure()
param azure_openai_key string
@secure()
param github_token string
@secure()
param google_api_key string
param llm_provider string
@secure()
param openai_api_key string
@secure()
param openai_org_id string
@metadata({azd: {
type: 'generate'
config: {length:22}
}
})
@secure()
param postgres_password string
@metadata({azd: {
type: 'generate'
config: {length:22,noSpecial:true}
}
})
@secure()
param redis_password string
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check what parameters resources.bicep actually expects
echo "=== Parameters declared in resources.bicep ==="
rg -n "^param " orchestration/AuthScript.AppHost/infra/resources.bicep || echo "resources.bicep not found or no params"

echo ""
echo "=== Check if secrets are wired elsewhere (e.g., Container Apps secrets) ==="
rg -l "postgres_password|redis_password|openai_api_key|athena_client" orchestration/AuthScript.AppHost/infra/

Repository: lvlup-sw/authscript

Length of output: 404


🏁 Script executed:

# First, let's see the full main.bicep file to check if these parameters are used anywhere
wc -l orchestration/AuthScript.AppHost/infra/main.bicep

Repository: lvlup-sw/authscript

Length of output: 115


🏁 Script executed:

# Check the full main.bicep to find any references to the secure parameters
cat orchestration/AuthScript.AppHost/infra/main.bicep

Repository: lvlup-sw/authscript

Length of output: 2759


🏁 Script executed:

# Also check what's in main.parameters.json
cat orchestration/AuthScript.AppHost/infra/main.parameters.json

Repository: lvlup-sw/authscript

Length of output: 1415


Secure parameters declared but not used anywhere in the template.

Lines 15-45 declare the following parameters:

  • athena_client_id, athena_client_secret, athena_practice_id
  • azure_openai_endpoint, azure_openai_key
  • github_token, google_api_key, llm_provider
  • openai_api_key, openai_org_id
  • postgres_password, redis_password

These are provided via main.parameters.json but are neither referenced in main.bicep nor passed to the resources module (lines 56-64). Since resources.bicep only accepts location, tags, and principalId, either these parameters need to be forwarded to that module (if the resources require them for Container Apps secrets), or they should be removed as dead code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@orchestration/AuthScript.AppHost/infra/main.bicep` around lines 15 - 45, The
declared secure parameters (athena_client_id, athena_client_secret,
athena_practice_id, azure_openai_endpoint, azure_openai_key, github_token,
google_api_key, llm_provider, openai_api_key, openai_org_id, postgres_password,
redis_password) are not used; either forward them into the resources module
invocation or remove them: if resources.bicep needs these as Container App
secrets, add matching params to resources.bicep and pass them from the
main.bicep module call (update the module call that currently passes location,
tags, principalId to also pass these parameter names), otherwise delete the
unused `@secure`() param declarations from main.bicep (and from
main.parameters.json) to eliminate dead code.

Navigation bug:
- Move DemoProvider to __root.tsx layout (shared across routes)
- Remove per-route DemoProvider wrappers from ehr-demo, fleet, case
- Wire SceneNav to TanStack Router (useNavigate + useLocation)
- Sync active scene pill from current URL pathname

Animations:
- KPI count-up animation via motion animate() with 800ms easeOut + stagger
- FleetCard/FleetView motion.div wrappers with layout + stagger animations
- CaseGraph sequential node reveal (Patient→Evidence→Criteria→Decision)
- AuthDetectionBanner replaced CSS animation with motion AnimatePresence

Accessibility:
- FleetCard: role="button", tabIndex, keyboard handler (Enter/Space)
- CasePipeline: aria-label on container, aria-hidden on decorative SVG
- CaseTimeline: aria-labels on phase indicators

Code quality:
- Extract getInitials() and getConfidenceColor() to shared formatUtils.ts
- Replace magic string 'LCD L34220' with LCD_L34220_POLICY.policyId
- Rename misleading _c to criterion in CaseGraph
- Deep-clone criteria templates in SeedDemoData.cs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (1)
apps/gateway/Gateway.API/Data/SeedDemoData.cs (1)

132-132: ⚠️ Potential issue | 🟠 Major

Make seed output deterministic across runs/locales.

Line 132 uses wall clock time, and Line 180 uses ambient culture formatting; both make output vary by execution date/machine locale, which breaks deterministic seed behavior.

Proposed fix
 namespace Gateway.API.Data;
 
+using System.Globalization;
 using Gateway.API.GraphQL.Models;
@@
 public static class SeedDemoData
 {
+    private static readonly DateTimeOffset DemoDataEpoch = new(2025, 01, 15, 12, 0, 0, TimeSpan.Zero);
@@
     public static List<PARequestModel> GenerateSeedRequests()
     {
         var rng = new Random(42); // deterministic seed
-        var now = DateTimeOffset.UtcNow;
+        var now = DemoDataEpoch;
@@
-                var serviceDate = now.AddDays(rng.Next(1, 30)).ToString("MMMM d, yyyy");
+                var serviceDate = now.AddDays(rng.Next(1, 30)).ToString("MMMM d, yyyy", CultureInfo.InvariantCulture);

Also applies to: 180-180

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/Gateway.API/Data/SeedDemoData.cs` at line 132, The seed uses
DateTimeOffset.UtcNow (variable now) and ambient culture formatting which makes
output non-deterministic; replace the dynamic now with a fixed, hard-coded
DateTimeOffset constant (e.g., new DateTimeOffset(2023,1,1,0,0,0,TimeSpan.Zero)
or a chosen epoch) used throughout SeedDemoData, and change any ToString/format
calls (the formatting at the later use around line with culture-dependent
output) to use explicit formatting with CultureInfo.InvariantCulture (e.g.,
.ToString("o", CultureInfo.InvariantCulture)) so the seed output is
deterministic across runs and locales.
🧹 Nitpick comments (8)
apps/dashboard/src/components/fleet/CasePipeline.tsx (2)

78-85: Consider adding explicit type="button".

While buttons default to type="submit" only within forms, explicitly setting type="button" is a defensive practice that prevents accidental form submissions if this component is ever composed within a form context.

Proposed diff
          <button
            key={stage.key}
+           type="button"
            data-testid={`pipeline-stage-${stage.key}`}
            onClick={() => onFilter(stage.key)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/CasePipeline.tsx` around lines 78 - 85,
The button rendered in the CasePipeline component (the element using
key={stage.key}, data-testid={`pipeline-stage-${stage.key}`} and onClick={() =>
onFilter(stage.key)}) should include an explicit type attribute to avoid
accidental form submissions; add type="button" to that <button> element (the one
also using isActive and cn(...) for className) so it behaves as a non-submitting
button when composed inside forms.

29-71: SVG animation approach works but has a minor consideration.

The inline <style> block defining the flowDot keyframe works fine for a single instance. If multiple CasePipeline components are rendered simultaneously, the keyframe definition duplicates—browsers handle this gracefully, but it's slightly inefficient.

For a demo component this is acceptable. If you ever need to scale this, consider moving the keyframe to a global CSS file or Tailwind's @layer utilities.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/CasePipeline.tsx` around lines 29 - 71,
The inline `@keyframes` flowDot inside the CasePipeline component's SVG causes
duplicate keyframe definitions when multiple CasePipeline instances render; move
the flowDot keyframe out of the component into a shared stylesheet (e.g., global
CSS or Tailwind `@layer` utilities) and remove the <style> block from the SVG so
the SVG's animated line still uses style={{ animation: 'flowDot 1.5s linear
infinite' }} but relies on the globally defined keyframes.
apps/gateway/Gateway.API/Data/SeedDemoData.cs (1)

155-161: Prefer strategy dispatch over status switch.

This status-based switch is clear, but a strategy map (status → template selector) will scale better as statuses evolve and aligns with project standards.

Refactor sketch
+private static readonly Dictionary<string, Func<Random, CriterionModel[]>> CriteriaTemplateByStatus = new(StringComparer.Ordinal)
+{
+    ["denied"] = _ => CriteriaTemplates[2],
+    ["approved"] = rng => rng.Next(2) == 0 ? CriteriaTemplates[0] : CriteriaTemplates[3],
+    ["processing"] = _ => CriteriaTemplates[4],
+};

@@
-                var criteriaTemplate = status switch
-                {
-                    "denied" => CriteriaTemplates[2],
-                    "approved" => rng.Next(2) == 0 ? CriteriaTemplates[0] : CriteriaTemplates[3],
-                    "processing" => CriteriaTemplates[4],
-                    _ => rng.Next(3) == 0 ? CriteriaTemplates[1] : CriteriaTemplates[0],
-                };
+                var criteriaTemplate = CriteriaTemplateByStatus.TryGetValue(status, out var selector)
+                    ? selector(rng)
+                    : (rng.Next(3) == 0 ? CriteriaTemplates[1] : CriteriaTemplates[0]);

As per coding guidelines, apps/gateway/**/*.cs should follow: “SOLID: … Strategy over switches …”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/Gateway.API/Data/SeedDemoData.cs` around lines 155 - 161,
Replace the status switch that assigns criteriaTemplate with a strategy
dispatch: create a dictionary/map from status (string) to a selector function
that takes rng and returns the chosen item from CriteriaTemplates, then look up
the selector for the current status and call it (falling back to a default
selector when missing). Update the code that currently uses the switch (variable
criteriaTemplate, status, rng, and CriteriaTemplates) to use that map (e.g.,
selectors.TryGetValue(status, out selector) ? selector(rng) :
defaultSelector(rng)) so new statuses can be added by registering new selector
entries instead of editing a switch.
apps/dashboard/src/routes/fleet.tsx (3)

81-81: Remove unused _graphqlStats fetch.

The usePAStats hook is called but the result is never used. This creates an unnecessary network request every 5 seconds (per the hook's refetch interval).

♻️ Proposed fix
   // GraphQL data (may not be available)
   const { data: graphqlRequests } = usePARequests();
-  const { data: _graphqlStats } = usePAStats();
   const { data: activity = [] } = useActivity();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/fleet.tsx` at line 81, The code calls the hook
usePAStats and assigns its result to _graphqlStats but never uses it, causing
unnecessary periodic fetches; remove the unused call to usePAStats (and remove
its import if now unused) from the file so no background refetch occurs—search
for the usePAStats invocation and the _graphqlStats variable in fleet.tsx and
delete that line (and clean up the import statement for usePAStats if orphaned).

85-94: Consider validating GraphQL status values.

The direct cast r.status as FleetStatus (line 90) assumes all GraphQL statuses besides 'draft' map to FleetStatus. If the backend introduces new statuses, this could produce invalid FleetStatus values silently.

For a demo, this is acceptable. For production, consider a validation function that maps unknown statuses to a default (e.g., 'processing').

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/fleet.tsx` around lines 85 - 94, The current
mapping in the useMemo for fleetData unsafely casts r.status to FleetStatus
which can produce invalid values if backend adds new statuses; create a small
validator/mapper function (e.g., mapGraphqlStatusToFleetStatus) that accepts the
GraphQL status from graphqlRequests and returns a validated FleetStatus
(explicitly handle known statuses and return a default like 'processing' for
unknowns), then use that mapper in the useMemo when creating FleetPARequest
entries instead of the direct cast; update references in the mapping logic and
keep generateFleetData as the fallback.

26-30: Redundant status check.

The r.status !== 'waiting_for_insurance' check is unnecessary since 'waiting_for_insurance' in counts is already false. Not blocking, but could simplify.

♻️ Simplified version
   for (const r of requests) {
-    if (r.status in counts && r.status !== 'waiting_for_insurance') {
+    if (r.status in counts) {
       counts[r.status as Exclude<FleetStatus, 'waiting_for_insurance'>] += 1;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/fleet.tsx` around lines 26 - 30, The loop over
requests has a redundant check — remove the unnecessary r.status !==
'waiting_for_insurance' condition inside the for (const r of requests) loop and
only rely on the existence check if (r.status in counts) before incrementing
counts; keep the explicit cast counts[r.status as Exclude<FleetStatus,
'waiting_for_insurance'>] += 1 to satisfy the type system while simplifying the
conditional.
apps/dashboard/src/routes/__tests__/case.test.tsx (1)

44-49: Consider adding type safety to the dynamic import.

The (mod as any).CaseDetailPage assertion loses type safety. A minor improvement would be to type the expected exports.

♻️ Proposed fix
 async function renderCaseDetailPage() {
-  const mod = await import('../case.$caseId');
-  // The module exports CaseDetailPage as the component
-  const Component = (mod as any).CaseDetailPage;
+  const mod = await import('../case.$caseId') as { CaseDetailPage: React.ComponentType };
+  const Component = mod.CaseDetailPage;
   return render(createElement(DemoProvider, null, createElement(Component)));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/__tests__/case.test.tsx` around lines 44 - 49, The
dynamic import in renderCaseDetailPage currently casts the module to any and
loses type safety; update the import to assert the module shape (e.g. as
Promise<{ CaseDetailPage: React.ComponentType<any> }>) so (mod).CaseDetailPage
has a typed signature, then reference that typed CaseDetailPage when creating
the element with DemoProvider and render; ensure the type covers the actual
props if any instead of using any and keep usages of render, createElement and
DemoProvider unchanged.
apps/dashboard/src/components/case/CaseGraph.tsx (1)

27-31: Extract toStatus and buildCaseGraphData to a separate file for Fast Refresh compatibility.

The static analysis correctly identifies that exporting non-component functions alongside components breaks Vite's Fast Refresh. Consider moving toStatus and buildCaseGraphData to a dedicated utility file (e.g., caseGraphUtils.ts).

♻️ Proposed structure

Create apps/dashboard/src/components/case/caseGraphUtils.ts:

import type { Node, Edge } from '@xyflow/react';
import type { PARequest } from '@/api/graphqlService';
import { DEMO_PA_RESULT_SOURCES, LCD_L34220_POLICY } from '@/lib/demoData';

export function toStatus(met: boolean | null): 'met' | 'not_met' | 'indeterminate' {
  if (met === true) return 'met';
  if (met === false) return 'not_met';
  return 'indeterminate';
}

export function buildCaseGraphData(paRequest: PARequest): {
  nodes: Node[];
  edges: Edge[];
} {
  // ... existing implementation
}

Then import into CaseGraph.tsx:

-export function toStatus(...) { ... }
-export function buildCaseGraphData(...) { ... }
+import { toStatus, buildCaseGraphData } from './caseGraphUtils';
+export { toStatus, buildCaseGraphData };

Also applies to: 37-40

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/case/CaseGraph.tsx` around lines 27 - 31,
Extract the pure helper functions toStatus and buildCaseGraphData into a new
module (e.g., caseGraphUtils.ts) and export them; update CaseGraph.tsx to import
these helpers instead of defining them alongside the component so Vite Fast
Refresh won’t be broken. Specifically, move the implementations of toStatus and
buildCaseGraphData (referenced in CaseGraph.tsx) into the new file, export both
functions, and replace the local definitions with imports from the new module;
ensure any type imports (Node, Edge, PARequest) and demo constants used by
buildCaseGraphData are also imported into caseGraphUtils.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/dashboard/src/components/ehr/__tests__/AuthDetectionBanner.test.tsx`:
- Around line 7-15: Replace the loose any types and the unused destructuring in
the test mocks: change the div mock signature from "({ children, ...props }:
any) => { ... }" to accept properly typed React props (e.g. use props:
React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode } or
React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>), stop
destructuring out motion fields (remove initial/animate/exit/transition
destructure) and spread the remaining props onto the div, and change
AnimatePresence from "({ children }: any) => <>{children}</>" to a typed
function component like "( { children }: { children?: React.ReactNode } ) =>
<>{children}</>" so there are no any types or unused bindings; update the mocks
named "div" and "AnimatePresence" in AuthDetectionBanner.test.tsx accordingly.

In `@apps/dashboard/src/components/ehr/AuthDetectionBanner.tsx`:
- Around line 20-36: The banner rendered by motion.div currently announces
nothing to assistive tech; add live-region/alert semantics to the animated
container (the motion.div that wraps the Authorization Determination Engine and
the PA message) by giving it an appropriate role (e.g., role="status" or
role="alert") and aria-live and aria-atomic attributes so screen readers
announce dynamic text like the PA message ("PA Required — {payer} {policyId}
applies to CPT {cptCode}") when it appears; keep the existing animation props
(initial/animate/exit/transition) and ensure the attributes are passed to the
motion.div element.

In `@apps/dashboard/src/components/fleet/__tests__/FleetCard.test.tsx`:
- Around line 6-19: The mock uses unsafe "any" types causing lint/build
failures; update the motion.div and AnimatePresence mock signatures to use
proper React types instead of any (e.g., for motion.div use
React.ComponentProps<'div'> or React.HTMLAttributes<HTMLDivElement> for the
props parameter and for AnimatePresence use { children: React.ReactNode }), keep
the same filtering of motion-specific keys (initial, animate, exit, transition,
layout, whileHover, whileTap, whileFocus, whileInView) and return a plain <div
{...domProps}>{children}</div>; ensure relevant React types are
imported/available in the test file and remove all "any" usages.

In `@apps/dashboard/src/components/fleet/__tests__/FleetView.test.tsx`:
- Around line 6-18: The mock currently destructures motion-only props into
unused variables and types props as any; update the mock for motion.div to use a
concrete div prop type and rename unused motion props with underscore prefixes
to satisfy the linter: change the parameter from "({ children, ...props }: any)"
to "(props: React.ComponentProps<'div'])", then inside destructure "const {
children, initial: _initial, animate: _animate, exit: _exit, transition:
_transition, layout: _layout, whileHover: _whileHover, whileTap: _whileTap,
whileFocus: _whileFocus, whileInView: _whileInView, ...domProps } = props" and
return <div {...domProps}>{children}</div>; similarly type AnimatePresence props
(e.g., React.PropsWithChildren) to remove any usage of any while keeping
behavior of motion.div and AnimatePresence mocks.

In `@apps/dashboard/src/routes/ehr-demo.tsx`:
- Around line 264-275: The "View in Fleet" button rendered when isComplete lacks
a click handler; either wire it to the demo context or disable it: import and
call the demo context's setScene (e.g., setScene('fleet')) in an onClick on that
button (or use your router's navigate/useNavigate to go to the Fleet route) so
clicking transitions to the Fleet scene, or if navigation is intentionally
omitted, add a disabled attribute and/or a TODO comment on the button to make
the intent explicit; update the button element that renders alongside isComplete
and keep the ArrowRight icon as-is.

---

Duplicate comments:
In `@apps/gateway/Gateway.API/Data/SeedDemoData.cs`:
- Line 132: The seed uses DateTimeOffset.UtcNow (variable now) and ambient
culture formatting which makes output non-deterministic; replace the dynamic now
with a fixed, hard-coded DateTimeOffset constant (e.g., new
DateTimeOffset(2023,1,1,0,0,0,TimeSpan.Zero) or a chosen epoch) used throughout
SeedDemoData, and change any ToString/format calls (the formatting at the later
use around line with culture-dependent output) to use explicit formatting with
CultureInfo.InvariantCulture (e.g., .ToString("o",
CultureInfo.InvariantCulture)) so the seed output is deterministic across runs
and locales.

---

Nitpick comments:
In `@apps/dashboard/src/components/case/CaseGraph.tsx`:
- Around line 27-31: Extract the pure helper functions toStatus and
buildCaseGraphData into a new module (e.g., caseGraphUtils.ts) and export them;
update CaseGraph.tsx to import these helpers instead of defining them alongside
the component so Vite Fast Refresh won’t be broken. Specifically, move the
implementations of toStatus and buildCaseGraphData (referenced in CaseGraph.tsx)
into the new file, export both functions, and replace the local definitions with
imports from the new module; ensure any type imports (Node, Edge, PARequest) and
demo constants used by buildCaseGraphData are also imported into
caseGraphUtils.ts.

In `@apps/dashboard/src/components/fleet/CasePipeline.tsx`:
- Around line 78-85: The button rendered in the CasePipeline component (the
element using key={stage.key}, data-testid={`pipeline-stage-${stage.key}`} and
onClick={() => onFilter(stage.key)}) should include an explicit type attribute
to avoid accidental form submissions; add type="button" to that <button> element
(the one also using isActive and cn(...) for className) so it behaves as a
non-submitting button when composed inside forms.
- Around line 29-71: The inline `@keyframes` flowDot inside the CasePipeline
component's SVG causes duplicate keyframe definitions when multiple CasePipeline
instances render; move the flowDot keyframe out of the component into a shared
stylesheet (e.g., global CSS or Tailwind `@layer` utilities) and remove the
<style> block from the SVG so the SVG's animated line still uses style={{
animation: 'flowDot 1.5s linear infinite' }} but relies on the globally defined
keyframes.

In `@apps/dashboard/src/routes/__tests__/case.test.tsx`:
- Around line 44-49: The dynamic import in renderCaseDetailPage currently casts
the module to any and loses type safety; update the import to assert the module
shape (e.g. as Promise<{ CaseDetailPage: React.ComponentType<any> }>) so
(mod).CaseDetailPage has a typed signature, then reference that typed
CaseDetailPage when creating the element with DemoProvider and render; ensure
the type covers the actual props if any instead of using any and keep usages of
render, createElement and DemoProvider unchanged.

In `@apps/dashboard/src/routes/fleet.tsx`:
- Line 81: The code calls the hook usePAStats and assigns its result to
_graphqlStats but never uses it, causing unnecessary periodic fetches; remove
the unused call to usePAStats (and remove its import if now unused) from the
file so no background refetch occurs—search for the usePAStats invocation and
the _graphqlStats variable in fleet.tsx and delete that line (and clean up the
import statement for usePAStats if orphaned).
- Around line 85-94: The current mapping in the useMemo for fleetData unsafely
casts r.status to FleetStatus which can produce invalid values if backend adds
new statuses; create a small validator/mapper function (e.g.,
mapGraphqlStatusToFleetStatus) that accepts the GraphQL status from
graphqlRequests and returns a validated FleetStatus (explicitly handle known
statuses and return a default like 'processing' for unknowns), then use that
mapper in the useMemo when creating FleetPARequest entries instead of the direct
cast; update references in the mapping logic and keep generateFleetData as the
fallback.
- Around line 26-30: The loop over requests has a redundant check — remove the
unnecessary r.status !== 'waiting_for_insurance' condition inside the for (const
r of requests) loop and only rely on the existence check if (r.status in counts)
before incrementing counts; keep the explicit cast counts[r.status as
Exclude<FleetStatus, 'waiting_for_insurance'>] += 1 to satisfy the type system
while simplifying the conditional.

In `@apps/gateway/Gateway.API/Data/SeedDemoData.cs`:
- Around line 155-161: Replace the status switch that assigns criteriaTemplate
with a strategy dispatch: create a dictionary/map from status (string) to a
selector function that takes rng and returns the chosen item from
CriteriaTemplates, then look up the selector for the current status and call it
(falling back to a default selector when missing). Update the code that
currently uses the switch (variable criteriaTemplate, status, rng, and
CriteriaTemplates) to use that map (e.g., selectors.TryGetValue(status, out
selector) ? selector(rng) : defaultSelector(rng)) so new statuses can be added
by registering new selector entries instead of editing a switch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bfc50bb2-a677-447b-b227-f4371ae7fda2

📥 Commits

Reviewing files that changed from the base of the PR and between a8a2aa7 and 1f27f80.

📒 Files selected for processing (24)
  • apps/dashboard/src/components/case/CaseGraph.tsx
  • apps/dashboard/src/components/case/CaseTimeline.tsx
  • apps/dashboard/src/components/case/PatientNode.tsx
  • apps/dashboard/src/components/demo/SceneNav.tsx
  • apps/dashboard/src/components/demo/__tests__/SceneNav.test.tsx
  • apps/dashboard/src/components/ehr/AuthDetectionBanner.tsx
  • apps/dashboard/src/components/ehr/__tests__/AuthDetectionBanner.test.tsx
  • apps/dashboard/src/components/fleet/CasePipeline.tsx
  • apps/dashboard/src/components/fleet/FleetCard.tsx
  • apps/dashboard/src/components/fleet/FleetView.tsx
  • apps/dashboard/src/components/fleet/KPICards.tsx
  • apps/dashboard/src/components/fleet/__tests__/FleetCard.test.tsx
  • apps/dashboard/src/components/fleet/__tests__/FleetView.test.tsx
  • apps/dashboard/src/components/fleet/__tests__/KPICards.test.tsx
  • apps/dashboard/src/lib/__tests__/formatUtils.test.ts
  • apps/dashboard/src/lib/formatUtils.ts
  • apps/dashboard/src/routes/__root.tsx
  • apps/dashboard/src/routes/__tests__/case.test.tsx
  • apps/dashboard/src/routes/__tests__/ehr-demo.test.tsx
  • apps/dashboard/src/routes/__tests__/fleet.test.tsx
  • apps/dashboard/src/routes/case.$caseId.tsx
  • apps/dashboard/src/routes/ehr-demo.tsx
  • apps/dashboard/src/routes/fleet.tsx
  • apps/gateway/Gateway.API/Data/SeedDemoData.cs
✅ Files skipped from review due to trivial changes (1)
  • apps/dashboard/src/lib/tests/formatUtils.test.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • apps/dashboard/src/components/case/CaseTimeline.tsx
  • apps/dashboard/src/components/demo/SceneNav.tsx
  • apps/dashboard/src/components/demo/tests/SceneNav.test.tsx
  • apps/dashboard/src/components/fleet/tests/KPICards.test.tsx
  • apps/dashboard/src/components/fleet/FleetCard.tsx
  • apps/dashboard/src/components/fleet/FleetView.tsx
  • apps/dashboard/src/routes/tests/fleet.test.tsx
  • apps/dashboard/src/components/case/PatientNode.tsx
  • apps/dashboard/src/components/fleet/KPICards.tsx

Comment on lines +7 to +15
div: ({ children, ...props }: any) => {
const {
initial, animate: _animate, exit, transition,
...domProps
} = props;
return <div {...domProps}>{children}</div>;
},
},
AnimatePresence: ({ children }: any) => <>{children}</>,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and get its content
fd "AuthDetectionBanner.test.tsx" apps/dashboard/

Repository: lvlup-sw/authscript

Length of output: 135


🏁 Script executed:

# Read the test file to see the actual code
cat -n "apps/dashboard/src/components/ehr/__tests__/AuthDetectionBanner.test.tsx" | head -40

Repository: lvlup-sw/authscript

Length of output: 1445


🏁 Script executed:

# Check if there's a linting config in the project
fd -e eslintrc -e eslint.config -e ".eslintrc*" apps/dashboard/

Repository: lvlup-sw/authscript

Length of output: 428


🏁 Script executed:

# Check tsconfig.json for strict mode settings
fd "tsconfig.json" apps/dashboard/ -x cat -n {}

Repository: lvlup-sw/authscript

Length of output: 866


🏁 Script executed:

# Also check the root tsconfig
cat -n "tsconfig.json" | head -30

Repository: lvlup-sw/authscript

Length of output: 108


🏁 Script executed:

# Search for ESLint configuration files in the repo
fd --full-path "eslint" . --type f | head -20

Repository: lvlup-sw/authscript

Length of output: 96


🏁 Script executed:

# Check for eslintrc or eslint config in package.json
fd "package.json" -x grep -A 10 "eslintConfig" {} 2>/dev/null | head -30

Repository: lvlup-sw/authscript

Length of output: 45


🏁 Script executed:

# Look for eslint config in common locations
ls -la | grep -i eslint

Repository: lvlup-sw/authscript

Length of output: 45


🏁 Script executed:

# Check the ESLint configuration in the dashboard
cat -n "apps/dashboard/eslint.config.js" | head -50

Repository: lvlup-sw/authscript

Length of output: 1167


🏁 Script executed:

# Also check for any stricter TypeScript checking in the dashboard tsconfig
fd "tsconfig.json" apps/ -x cat -n {}

Repository: lvlup-sw/authscript

Length of output: 866


Fix the mock typing to resolve TypeScript strict mode violations.

Lines 7 and 15 use any types which violate the ESLint no-explicit-any rule, and the destructuring pattern on lines 8–11 creates an unused binding (_animate) that TypeScript's noUnusedLocals flag catches. Replace any with proper React types and remove the problematic destructuring:

Proposed fix
+import type { HTMLAttributes, PropsWithChildren } from 'react';
 import { describe, it, expect, vi } from 'vitest';
 import { render, screen } from '@testing-library/react';

 // Mock motion/react for test environment
+type MotionDivMockProps = HTMLAttributes<HTMLDivElement> & {
+  initial?: unknown;
+  animate?: unknown;
+  exit?: unknown;
+  transition?: unknown;
+};
+
 vi.mock('motion/react', () => ({
   motion: {
-    div: ({ children, ...props }: any) => {
-      const {
-        initial, animate: _animate, exit, transition,
-        ...domProps
-      } = props;
+    div: ({ children, ...props }: PropsWithChildren<MotionDivMockProps>) => {
+      const domProps: MotionDivMockProps = { ...props };
+      delete domProps.initial;
+      delete domProps.animate;
+      delete domProps.exit;
+      delete domProps.transition;
       return <div {...domProps}>{children}</div>;
     },
   },
-  AnimatePresence: ({ children }: any) => <>{children}</>,
+  AnimatePresence: ({ children }: PropsWithChildren) => <>{children}</>,
 }));
🧰 Tools
🪛 GitHub Actions: CI

[error] 9-9: 'initial' is assigned a value but never used. (no-unused-vars)


[warning] 7-7: Unexpected any. Specify a different type


[error] 9-9: 'initial' is assigned a value but never used. (no-unused-vars)

🪛 GitHub Check: Dashboard Build & Test

[failure] 9-9:
'transition' is assigned a value but never used


[failure] 9-9:
'exit' is assigned a value but never used


[failure] 9-9:
'_animate' is assigned a value but never used


[failure] 9-9:
'initial' is assigned a value but never used

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/ehr/__tests__/AuthDetectionBanner.test.tsx`
around lines 7 - 15, Replace the loose any types and the unused destructuring in
the test mocks: change the div mock signature from "({ children, ...props }:
any) => { ... }" to accept properly typed React props (e.g. use props:
React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode } or
React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>), stop
destructuring out motion fields (remove initial/animate/exit/transition
destructure) and spread the remaining props onto the div, and change
AnimatePresence from "({ children }: any) => <>{children}</>" to a typed
function component like "( { children }: { children?: React.ReactNode } ) =>
<>{children}</>" so there are no any types or unused bindings; update the mocks
named "div" and "AnimatePresence" in AuthDetectionBanner.test.tsx accordingly.

Comment on lines +20 to +36
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
className="overflow-hidden rounded-lg border border-amber-200 bg-amber-50 shadow-sm"
>
<div className="px-4 py-1">
<span className="text-[10px] font-semibold uppercase tracking-widest text-teal-600">
Authorization Determination Engine
</span>
</div>
<div className="flex items-center gap-3 border-t border-amber-100 px-4 py-2.5">
<ShieldAlert className="h-5 w-5 shrink-0 text-amber-600" />
<span className="text-sm font-medium text-amber-900">
PA Required — {payer} {policyId} applies to CPT {cptCode}
</span>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Announce the PA-required state to screen readers.

This status appears dynamically but has no live-region semantics, so assistive tech may miss it. Add alert semantics on the animated container.

Proposed fix
         <motion.div
+          role="alert"
+          aria-atomic="true"
           initial={{ opacity: 0, y: -20 }}
           animate={{ opacity: 1, y: 0 }}
           exit={{ opacity: 0, y: -20 }}
           transition={{ duration: 0.3 }}
           className="overflow-hidden rounded-lg border border-amber-200 bg-amber-50 shadow-sm"
         >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
className="overflow-hidden rounded-lg border border-amber-200 bg-amber-50 shadow-sm"
>
<div className="px-4 py-1">
<span className="text-[10px] font-semibold uppercase tracking-widest text-teal-600">
Authorization Determination Engine
</span>
</div>
<div className="flex items-center gap-3 border-t border-amber-100 px-4 py-2.5">
<ShieldAlert className="h-5 w-5 shrink-0 text-amber-600" />
<span className="text-sm font-medium text-amber-900">
PA Required {payer} {policyId} applies to CPT {cptCode}
</span>
<motion.div
role="alert"
aria-atomic="true"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
className="overflow-hidden rounded-lg border border-amber-200 bg-amber-50 shadow-sm"
>
<div className="px-4 py-1">
<span className="text-[10px] font-semibold uppercase tracking-widest text-teal-600">
Authorization Determination Engine
</span>
</div>
<div className="flex items-center gap-3 border-t border-amber-100 px-4 py-2.5">
<ShieldAlert className="h-5 w-5 shrink-0 text-amber-600" />
<span className="text-sm font-medium text-amber-900">
PA Required {payer} {policyId} applies to CPT {cptCode}
</span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/ehr/AuthDetectionBanner.tsx` around lines 20 -
36, The banner rendered by motion.div currently announces nothing to assistive
tech; add live-region/alert semantics to the animated container (the motion.div
that wraps the Authorization Determination Engine and the PA message) by giving
it an appropriate role (e.g., role="status" or role="alert") and aria-live and
aria-atomic attributes so screen readers announce dynamic text like the PA
message ("PA Required — {payer} {policyId} applies to CPT {cptCode}") when it
appears; keep the existing animation props (initial/animate/exit/transition) and
ensure the attributes are passed to the motion.div element.

Comment on lines +6 to +19
vi.mock('motion/react', () => ({
motion: {
div: ({ children, ...props }: any) => {
// Filter out motion-specific props that are not valid DOM attributes
const {
initial, animate: _animate, exit, transition, layout,
whileHover, whileTap, whileFocus, whileInView,
...domProps
} = props;
return <div {...domProps}>{children}</div>;
},
},
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same lint errors and any usage as FleetView.test.tsx.

Apply the same fix here to address the build failures and guideline violations.

🛠️ Proposed fix
-vi.mock('motion/react', () => ({
-  motion: {
-    div: ({ children, ...props }: any) => {
-      // Filter out motion-specific props that are not valid DOM attributes
-      const {
-        initial, animate: _animate, exit, transition, layout,
-        whileHover, whileTap, whileFocus, whileInView,
-        ...domProps
-      } = props;
-      return <div {...domProps}>{children}</div>;
-    },
-  },
-  AnimatePresence: ({ children }: any) => <>{children}</>,
-}));
+vi.mock('motion/react', () => ({
+  motion: {
+    div: ({
+      children,
+      initial: _initial,
+      animate: _animate,
+      exit: _exit,
+      transition: _transition,
+      layout: _layout,
+      whileHover: _whileHover,
+      whileTap: _whileTap,
+      whileFocus: _whileFocus,
+      whileInView: _whileInView,
+      ...domProps
+    }: React.ComponentProps<'div'> & Record<string, unknown>) => {
+      return <div {...domProps}>{children}</div>;
+    },
+  },
+  AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
+}));

As per coding guidelines: "no any".

🧰 Tools
🪛 GitHub Check: Dashboard Build & Test

[failure] 12-12:
'whileHover' is assigned a value but never used


[failure] 11-11:
'layout' is assigned a value but never used


[failure] 11-11:
'transition' is assigned a value but never used


[failure] 11-11:
'exit' is assigned a value but never used


[failure] 11-11:
'_animate' is assigned a value but never used


[failure] 11-11:
'initial' is assigned a value but never used

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/__tests__/FleetCard.test.tsx` around
lines 6 - 19, The mock uses unsafe "any" types causing lint/build failures;
update the motion.div and AnimatePresence mock signatures to use proper React
types instead of any (e.g., for motion.div use React.ComponentProps<'div'> or
React.HTMLAttributes<HTMLDivElement> for the props parameter and for
AnimatePresence use { children: React.ReactNode }), keep the same filtering of
motion-specific keys (initial, animate, exit, transition, layout, whileHover,
whileTap, whileFocus, whileInView) and return a plain <div
{...domProps}>{children}</div>; ensure relevant React types are
imported/available in the test file and remove all "any" usages.

Comment on lines +6 to +18
vi.mock('motion/react', () => ({
motion: {
div: ({ children, ...props }: any) => {
const {
initial, animate: _animate, exit, transition, layout,
whileHover, whileTap, whileFocus, whileInView,
...domProps
} = props;
return <div {...domProps}>{children}</div>;
},
},
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix lint errors and avoid any in mock.

The build is failing due to unused destructured variables. Also, any violates the coding guidelines. Consider using a proper type or suppressing only the motion-specific props with an underscore prefix.

🛠️ Proposed fix
-vi.mock('motion/react', () => ({
-  motion: {
-    div: ({ children, ...props }: any) => {
-      const {
-        initial, animate: _animate, exit, transition, layout,
-        whileHover, whileTap, whileFocus, whileInView,
-        ...domProps
-      } = props;
-      return <div {...domProps}>{children}</div>;
-    },
-  },
-  AnimatePresence: ({ children }: any) => <>{children}</>,
-}));
+vi.mock('motion/react', () => ({
+  motion: {
+    div: ({
+      children,
+      initial: _initial,
+      animate: _animate,
+      exit: _exit,
+      transition: _transition,
+      layout: _layout,
+      whileHover: _whileHover,
+      whileTap: _whileTap,
+      whileFocus: _whileFocus,
+      whileInView: _whileInView,
+      ...domProps
+    }: React.ComponentProps<'div'> & Record<string, unknown>) => {
+      return <div {...domProps}>{children}</div>;
+    },
+  },
+  AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
+}));

As per coding guidelines: "no any".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vi.mock('motion/react', () => ({
motion: {
div: ({ children, ...props }: any) => {
const {
initial, animate: _animate, exit, transition, layout,
whileHover, whileTap, whileFocus, whileInView,
...domProps
} = props;
return <div {...domProps}>{children}</div>;
},
},
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
vi.mock('motion/react', () => ({
motion: {
div: ({
children,
initial: _initial,
animate: _animate,
exit: _exit,
transition: _transition,
layout: _layout,
whileHover: _whileHover,
whileTap: _whileTap,
whileFocus: _whileFocus,
whileInView: _whileInView,
...domProps
}: React.ComponentProps<'div'> & Record<string, unknown>) => {
return <div {...domProps}>{children}</div>;
},
},
AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/fleet/__tests__/FleetView.test.tsx` around
lines 6 - 18, The mock currently destructures motion-only props into unused
variables and types props as any; update the mock for motion.div to use a
concrete div prop type and rename unused motion props with underscore prefixes
to satisfy the linter: change the parameter from "({ children, ...props }: any)"
to "(props: React.ComponentProps<'div'])", then inside destructure "const {
children, initial: _initial, animate: _animate, exit: _exit, transition:
_transition, layout: _layout, whileHover: _whileHover, whileTap: _whileTap,
whileFocus: _whileFocus, whileInView: _whileInView, ...domProps } = props" and
return <div {...domProps}>{children}</div>; similarly type AnimatePresence props
(e.g., React.PropsWithChildren) to remove any usage of any while keeping
behavior of motion.div and AnimatePresence mocks.

Comment on lines +264 to +275
{/* Fleet transition trigger — visible after PA submission completes */}
{isComplete && (
<div className="flex justify-center pt-2">
<button
type="button"
className="inline-flex items-center gap-2 rounded-lg bg-teal-600 px-5 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-teal-700"
>
View in Fleet
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</button>
</div>
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

"View in Fleet" button has no click handler.

The button renders correctly when isComplete but doesn't navigate anywhere. If this is intentional for the demo phase, consider adding a disabled state or a TODO comment. Otherwise, wire it to the demo context's setScene or use router navigation.

🔧 Proposed fix using demo context
+import { useDemoContext } from '@/components/demo/DemoProvider';
+import { useNavigate } from '@tanstack/react-router';

 export function EhrDemoPage() {
   const flow = useEhrDemoFlow();
+  const { setScene } = useDemoContext();
+  const navigate = useNavigate();
   // ...

   {isComplete && (
     <div className="flex justify-center pt-2">
       <button
         type="button"
+        onClick={() => {
+          setScene('fleet');
+          navigate({ to: '/fleet' });
+        }}
         className="inline-flex items-center gap-2 rounded-lg bg-teal-600 px-5 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-teal-700"
       >
         View in Fleet
         <ArrowRight className="h-4 w-4" aria-hidden="true" />
       </button>
     </div>
   )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/routes/ehr-demo.tsx` around lines 264 - 275, The "View in
Fleet" button rendered when isComplete lacks a click handler; either wire it to
the demo context or disable it: import and call the demo context's setScene
(e.g., setScene('fleet')) in an onClick on that button (or use your router's
navigate/useNavigate to go to the Fleet route) so clicking transitions to the
Fleet scene, or if navigation is intentionally omitted, add a disabled attribute
and/or a TODO comment on the button to make the intent explicit; update the
button element that renders alongside isComplete and keep the ArrowRight icon
as-is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant