Skip to content

feat(risk-assessment): assessment results view and query hooks#8

Open
mrizzi wants to merge 12 commits intoNISTfrom
JIRAPLAY-1380
Open

feat(risk-assessment): assessment results view and query hooks#8
mrizzi wants to merge 12 commits intoNISTfrom
JIRAPLAY-1380

Conversation

@mrizzi
Copy link
Copy Markdown
Owner

@mrizzi mrizzi commented Mar 31, 2026

Summary

  • Add query hooks for risk assessment API (useFetchRiskAssessmentsByGroup, useFetchRiskAssessmentResults, useCreateRiskAssessmentMutation, useDownloadAssessmentDocument)
  • Implement overall score card with submission date and action buttons (Start New Assessment, Download Assessment)
  • Add criteria summary table with color-coded Label badges for completeness and icon+text for risk level
  • Integrate results view into Product Risk Assessment tab, switching between wizard (in progress) and results (completed)
  • Replace window.location.reload() with React Query invalidation in mutation success callback
  • Fix all API endpoints and TypeScript types to match backend: group-based lookup, nested results structure, correct response shapes
  • Align results view with Figma mockup: render inside wizard layout, add download button, wrap table in Card, use status icons for risk level

Implements JIRAPLAY-1380
Implements JIRAPLAY-1394
Implements JIRAPLAY-1396
Implements JIRAPLAY-1406

Test plan

  • Verify Product Risk Assessment tab loads without errors when no assessments exist
  • Verify "Start New Assessment" button creates assessment via POST /api/v2/risk-assessment
  • Verify wizard displays with correct assessment ID after creation
  • Verify results display inside wizard layout with left step navigation visible
  • Verify overall score card shows percentage, risk level, and completion date
  • Verify both "Start New Assessment" and "Download Assessment" buttons are visible below the score
  • Verify criteria summary table is wrapped in a Card with "Criteria Summary" title
  • Verify risk level column uses icon + text format (not Label badges)
  • Verify completeness column uses color-coded Label badges
  • Verify wizard footer (Back/Next) is visible in results view

🤖 Generated with Claude Code

Add query hooks for risk assessment API (fetch assessment, fetch results,
create assessment, download report). Implement overall score card with
submission date and action buttons, and criteria summary table with
color-coded completeness and risk level labels. Integrate results view
into the Product Risk Assessment tab, switching between wizard (in
progress) and results (completed) based on assessment status.

Implements JIRAPLAY-1380

Assisted-by: Claude Code
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 31, 2026

Reviewer's Guide

Implements a completed risk assessment results view and supporting React Query hooks, wiring it into the Product Risk Assessment tab so it can switch between the wizard for in‑progress assessments and a results dashboard for completed assessments, including PDF download and criteria summary table.

Sequence diagram for loading ProductRiskAssessment and switching between wizard and results

sequenceDiagram
  actor User
  participant ProductRiskAssessment
  participant FetchRiskAssessmentHook
  participant RiskAssessmentAPI
  participant AssessmentWizard
  participant AssessmentResults

  User->>ProductRiskAssessment: openRiskAssessmentTab(riskAssessmentId)
  ProductRiskAssessment->>FetchRiskAssessmentHook: useFetchRiskAssessment(riskAssessmentId)
  FetchRiskAssessmentHook->>RiskAssessmentAPI: GET /api/v2/risk-assessment/{id}
  RiskAssessmentAPI-->>FetchRiskAssessmentHook: RiskAssessment
  FetchRiskAssessmentHook-->>ProductRiskAssessment: riskAssessment,isFetching,fetchError

  alt isFetching
    ProductRiskAssessment-->>User: show Spinner
  else fetchError
    ProductRiskAssessment-->>User: show StateError
  else status is completed
    ProductRiskAssessment->>AssessmentResults: render(riskAssessmentId,onStartNewAssessment)
  else status is in_progress
    ProductRiskAssessment->>AssessmentWizard: render(riskAssessmentId)
  end

  User->>ProductRiskAssessment: click StartNewAssessment
  ProductRiskAssessment->>createRiskAssessmentMutation: mutate(riskAssessmentId)
  createRiskAssessmentMutation->>RiskAssessmentAPI: POST /api/v2/risk-assessment { groupId }
  RiskAssessmentAPI-->>createRiskAssessmentMutation: RiskAssessment
  createRiskAssessmentMutation-->>ProductRiskAssessment: onSuccess(response)
  ProductRiskAssessment->>Window: location.reload()
Loading

Sequence diagram for AssessmentResults data loading and PDF download

sequenceDiagram
  actor User
  participant AssessmentResults
  participant FetchRiskAssessmentResultsHook
  participant DownloadAssessmentHook
  participant RiskAssessmentAPI
  participant Browser

  User->>AssessmentResults: viewCompletedAssessment(riskAssessmentId)
  AssessmentResults->>FetchRiskAssessmentResultsHook: useFetchRiskAssessmentResults(riskAssessmentId)
  FetchRiskAssessmentResultsHook->>RiskAssessmentAPI: GET /api/v2/risk-assessment/{id}/results
  RiskAssessmentAPI-->>FetchRiskAssessmentResultsHook: RiskAssessmentResults
  FetchRiskAssessmentResultsHook-->>AssessmentResults: results,isFetching,fetchError

  alt isFetching
    AssessmentResults-->>User: show Spinner
  else fetchError
    AssessmentResults-->>User: show StateError
  else results loaded
    AssessmentResults-->>User: show overallScore,submittedDate,CriteriaSummaryTable
  end

  User->>AssessmentResults: click DownloadAssessment
  AssessmentResults->>DownloadAssessmentHook: download()
  DownloadAssessmentHook->>RiskAssessmentAPI: GET /api/v2/risk-assessment/{id}/report Accept application/pdf
  RiskAssessmentAPI-->>DownloadAssessmentHook: Blob(pdf)
  DownloadAssessmentHook->>Browser: createObjectURL(blob)
  DownloadAssessmentHook->>Browser: clickHiddenLink(download risk-assessment-{id}.pdf)
  Browser-->>User: saveOrOpenPDF
Loading

Class diagram for risk assessment types, hooks, and UI components

classDiagram
  class RiskAssessment {
    +string id
    +string status
    +string submittedAt
    +number overallScore
  }

  class CriterionResult {
    +string criterion
    +string completeness
    +string riskLevel
    +number score
  }

  class RiskAssessmentResults {
    +number overallScore
    +string submittedAt
    +CriterionResult[] criteria
  }

  class useFetchRiskAssessment {
    +RiskAssessment riskAssessment
    +boolean isFetching
    +AxiosError fetchError
    +useFetchRiskAssessment(id string) useFetchRiskAssessment
  }

  class useFetchRiskAssessmentResults {
    +RiskAssessmentResults results
    +boolean isFetching
    +AxiosError fetchError
    +useFetchRiskAssessmentResults(id string) useFetchRiskAssessmentResults
  }

  class useCreateRiskAssessmentMutation {
    +mutate(groupId string) void
    +useCreateRiskAssessmentMutation(onSuccess function,onError function) useCreateRiskAssessmentMutation
  }

  class useDownloadAssessment {
    +download() void
    +useDownloadAssessment(id string) useDownloadAssessment
  }

  class ProductRiskAssessment {
    +string riskAssessmentId
    +handleStartNewAssessment() void
    +render() ReactNode
  }

  class AssessmentResults {
    +string riskAssessmentId
    +onStartNewAssessment() void
    +render() ReactNode
  }

  class CriteriaSummaryTable {
    +CriterionResult[] criteria
    +render() ReactNode
  }

  class AssessmentWizard {
    +string riskAssessmentId
    +render() ReactNode
  }

  RiskAssessmentResults "1" o-- "*" CriterionResult

  ProductRiskAssessment ..> useFetchRiskAssessment : uses
  ProductRiskAssessment ..> useCreateRiskAssessmentMutation : uses
  ProductRiskAssessment ..> AssessmentWizard : renders
  ProductRiskAssessment ..> AssessmentResults : renders

  AssessmentResults ..> useFetchRiskAssessmentResults : uses
  AssessmentResults ..> useDownloadAssessment : uses
  AssessmentResults ..> CriteriaSummaryTable : renders

  CriteriaSummaryTable ..> CriterionResult : displays

  useFetchRiskAssessment ..> RiskAssessment : returns
  useFetchRiskAssessmentResults ..> RiskAssessmentResults : returns
  useCreateRiskAssessmentMutation ..> RiskAssessment : creates
  useDownloadAssessment ..> RiskAssessment : downloads report for
Loading

File-Level Changes

Change Details Files
Wire ProductRiskAssessment view to backend status and switch between wizard and results.
  • Add data fetching for a single risk assessment using a new query hook.
  • Show a loading spinner and error state while fetching the assessment.
  • Determine completion status from the fetched assessment and conditionally render the new results view or the existing wizard.
  • Add a mutation-based handler to start a new assessment and refresh the page on success.
client/src/app/pages/sbom-group-details/product-risk-assessment.tsx
Introduce an AssessmentResults component that displays overall score, submission date, and actions, plus a criteria summary table.
  • Fetch assessment results via a dedicated query hook and handle loading, error, and empty states.
  • Render an overall score card with percentage display and formatted submission date.
  • Add primary actions for starting a new assessment via callback and downloading the assessment via a download hook.
  • Embed the criteria summary table and pass criterion results from the fetched data.
client/src/app/pages/sbom-group-details/components/assessment-results.tsx
Add React Query hooks for fetching, creating, and downloading risk assessments and their results.
  • Define TypeScript interfaces for risk assessments, criteria results, and result payloads.
  • Implement useFetchRiskAssessment and useFetchRiskAssessmentResults using axios and React Query, keyed by assessment id.
  • Implement useCreateRiskAssessmentMutation that posts a new assessment, invalidates cached queries, and delegates success/error handling to callbacks.
  • Implement useDownloadAssessment helper that retrieves a PDF blob, creates a temporary download link, and triggers a file download.
client/src/app/queries/risk-assessments.ts
Add a CriteriaSummaryTable component to present criteria details with color-coded labels for completeness and risk level.
  • Define a compact PatternFly table with columns for criterion, completeness, risk level, and score.
  • Compute Label colors for completeness states (Complete, Partial, Missing) and risk levels (Very high, High, Moderate, Low).
  • Render table rows from the criteria array using the criterion as the React key and apply color-coded PatternFly Labels.
client/src/app/pages/sbom-group-details/components/criteria-summary-table.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 5 issues, and left some high level feedback:

  • In ProductRiskAssessment, the useCreateRiskAssessmentMutation success handler forces a full window.location.reload(), which is heavy-handed for an SPA; consider driving the UI from query invalidation/returned data or local state so the view updates without a full page refresh.
  • The useCreateRiskAssessmentMutation and useDownloadAssessment helpers silently ignore/absorb errors (empty onError callback and no error handling in download); consider surfacing failures via StateError or a notification/toast so users get feedback when actions fail.
  • In riskLevelColor, the Label color for the 'Low' case is set to 'grey', but PatternFly uses 'gray' as the valid color token; updating this avoids relying on an unsupported value and keeps styling consistent.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ProductRiskAssessment`, the `useCreateRiskAssessmentMutation` success handler forces a full `window.location.reload()`, which is heavy-handed for an SPA; consider driving the UI from query invalidation/returned data or local state so the view updates without a full page refresh.
- The `useCreateRiskAssessmentMutation` and `useDownloadAssessment` helpers silently ignore/absorb errors (empty `onError` callback and no error handling in `download`); consider surfacing failures via `StateError` or a notification/toast so users get feedback when actions fail.
- In `riskLevelColor`, the `Label` color for the 'Low' case is set to `'grey'`, but PatternFly uses `'gray'` as the valid color token; updating this avoids relying on an unsupported value and keeps styling consistent.

## Individual Comments

### Comment 1
<location path="client/src/app/pages/sbom-group-details/product-risk-assessment.tsx" line_range="24-25" />
<code_context>
+  const { riskAssessment, isFetching, fetchError } =
+    useFetchRiskAssessment(riskAssessmentId);
+
+  const createMutation = useCreateRiskAssessmentMutation(
+    () => {
+      window.location.reload();
+    },
</code_context>
<issue_to_address>
**suggestion (performance):** Avoid hard page reload and rely on query invalidation / local state to show the new assessment.

`window.location.reload()` on mutation success is heavy-handed and bypasses React Query’s caching. Since `useCreateRiskAssessmentMutation` already invalidates `RiskAssessmentsQueryKey`, you can instead update local state (e.g. clear the completed assessment or use the returned `RiskAssessment` to switch back to the wizard) and rely on React Query to re-fetch, avoiding a full-page refresh and improving UX.

Suggested implementation:

```typescript
  const createMutation = useCreateRiskAssessmentMutation(
    () => {},
    () => {},
  );

```

Depending on how `useFetchRiskAssessment` and `useCreateRiskAssessmentMutation` are implemented, you may want to:
1. Ensure `useCreateRiskAssessmentMutation` invalidates the relevant React Query keys (`RiskAssessmentsQueryKey` or similar) on success so that `useFetchRiskAssessment(riskAssessmentId)` refetches automatically.
2. If the UI differentiates between an "in-progress wizard" and "completed results" via local state, wire the `onSuccess` callback to update that state (e.g. reset wizard state or navigate back to the wizard view) instead of using `window.location.reload()`.
</issue_to_address>

### Comment 2
<location path="client/src/app/queries/risk-assessments.ts" line_range="57-59" />
<code_context>
+  };
+};
+
+export const useCreateRiskAssessmentMutation = (
+  onSuccess: (response: RiskAssessment) => void,
+  onError: (err: AxiosError) => void,
+) => {
+  const queryClient = useQueryClient();
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Align mutation error typing with React Query’s `onError` signature.

This callback assumes all errors are `AxiosError`, but React Query’s `onError` receives `unknown` and `mutationFn` can throw non-Axios errors. That can lead callers to rely on Axios-specific fields that may not exist at runtime. Consider using `(err: unknown) => void` and narrowing inside, or explicitly casting only where you’re certain the error is Axios-based.

Suggested implementation:

```typescript
export const useCreateRiskAssessmentMutation = (
  onSuccess: (response: RiskAssessment) => void,
  onError: (err: unknown) => void,
) => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: async (groupId: string) => {
      const response = await axios.post<RiskAssessment>(RISK_ASSESSMENTS, {
        groupId,
      });
      return response.data;
    },
    onSuccess: async (response) => {
      await queryClient.invalidateQueries({
        queryKey: [RiskAssessmentsQueryKey],

```

Any callers of `useCreateRiskAssessmentMutation` that currently type their error callback parameter as `AxiosError` should be updated to accept `unknown` instead, and then narrow internally using e.g. `axios.isAxiosError(err)` or type guards before accessing Axios-specific properties.
</issue_to_address>

### Comment 3
<location path="client/src/app/queries/risk-assessments.ts" line_range="79-82" />
<code_context>
+  });
+};
+
+export const useDownloadAssessment = (id?: string) => {
+  const download = async () => {
+    if (!id) return;
+    const response = await axios.get(`${RISK_ASSESSMENTS}/${id}/report`, {
+      responseType: "blob",
+      headers: { Accept: "application/pdf" },
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Add basic error handling around the download request to avoid silent failures.

If this request fails (e.g., network/server error), the user sees no indication. Consider wrapping the axios call in try/catch, showing an error state (toast/modal), and logging failures for observability.

Suggested implementation:

```typescript
export const useDownloadAssessment = (id?: string) => {
  const download = async () => {
    if (!id) return;

    try {
      const response = await axios.get(`${RISK_ASSESSMENTS}/${id}/report`, {
        responseType: "blob",
        headers: { Accept: "application/pdf" },
      });

      const url = window.URL.createObjectURL(new Blob([response.data]));
      const link = document.createElement("a");
      link.href = url;
      link.setAttribute("download", `risk-assessment-${id}.pdf`);
      document.body.appendChild(link);
      link.click();
      link.remove();
      window.URL.revokeObjectURL(url);
    } catch (error) {
      // Basic error handling to avoid silent failures and aid observability
      // Replace alert/console.error with app-wide toast or modal if available
      // e.g., toast.error("Unable to download risk assessment report");
      console.error("Failed to download risk assessment report", error);
      alert("Unable to download risk assessment report. Please try again.");
    }
  };

```

If the application already has a centralized notification/toast system (e.g., a `useToast` hook or similar), replace the `alert` call inside the `catch` block with that mechanism to match existing UX patterns, and add/import the appropriate hook or helper at the top of this file.
</issue_to_address>

### Comment 4
<location path="client/src/app/pages/sbom-group-details/components/criteria-summary-table.tsx" line_range="12-10" />
<code_context>
+  criteria: CriterionResult[];
+}
+
+const completenessColor = (value: CriterionResult["completeness"]) => {
+  switch (value) {
+    case "Complete":
+      return "green";
+    case "Partial":
+      return "blue";
+    case "Missing":
+      return "yellow";
+  }
+};
+
</code_context>
<issue_to_address>
**suggestion:** Provide an explicit default in `completenessColor` to guard against future enum extensions.

The switch is exhaustive today, but if `completeness` gets a new value later this will start returning `undefined` and silently fall back to the label’s default color. Adding a `default` (or a `never` check) would make the behavior explicit and safer against future enum changes.
</issue_to_address>

### Comment 5
<location path="client/src/app/pages/sbom-group-details/components/criteria-summary-table.tsx" line_range="23-33" />
<code_context>
+  }
+};
+
+const riskLevelColor = (value: CriterionResult["riskLevel"]) => {
+  switch (value) {
+    case "Very high":
+    case "High":
+      return "red";
+    case "Moderate":
+      return "orange";
+    case "Low":
+      return "grey";
+  }
+};
+
</code_context>
<issue_to_address>
**suggestion:** Add a safe default branch in `riskLevelColor` for unexpected risk levels.

Right now, any future or unexpected `riskLevel` will fall through and return `undefined`, relying on downstream fallbacks. Adding a default that maps unknown values to a neutral color would make misconfigurations more visible and predictable.

```suggestion
const riskLevelColor = (value: CriterionResult["riskLevel"]) => {
  switch (value) {
    case "Very high":
    case "High":
      return "red";
    case "Moderate":
      return "orange";
    case "Low":
      return "grey";
    default:
      // Fallback for unexpected or future risk levels
      return "grey";
  }
};
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@mrizzi
Copy link
Copy Markdown
Owner Author

mrizzi commented Mar 31, 2026

Verification Report for JIRAPLAY-1380 (commit f9bd36a)

Check Result Details
Review Feedback WARN 5 comments from sourcery-ai: 1 upgraded to code change request (sub-task JIRAPLAY-1394), 2 suggestions, 2 nits
Root-Cause Investigation DONE implement-task sibling parity gap — root-cause task JIRAPLAY-1395 created
Scope Containment PASS All 4 files match task specification
Diff Size PASS 310 additions, 2 deletions across 4 files — appropriate for scope
Commit Traceability PASS Commit references Implements JIRAPLAY-1380
Sensitive Patterns PASS No secrets or credentials detected
CI Status PASS checks, ci, coverage all pass
Acceptance Criteria PASS 7 of 7 criteria met
Verification Commands PASS Build compiles (pre-existing errors only), Biome check passes

Overall: WARN

One review feedback item upgraded to code change request: window.location.reload() should be replaced with React Query invalidation pattern. Sub-task JIRAPLAY-1394 created to track the fix.


This comment was AI-generated by sdlc-workflow/verify-pr v0.5.5.

mrizzi added 2 commits March 31, 2026 15:09
… invalidation

Remove window.location.reload() from the mutation success callback in
ProductRiskAssessment. The useCreateRiskAssessmentMutation hook already
invalidates RiskAssessmentsQueryKey on success, so React Query will
automatically re-fetch the assessment data without a full page reload.

Implements JIRAPLAY-1394

Assisted-by: Claude Code
Fix all risk assessment query hooks, TypeScript interfaces, and component
logic to match the actual backend API:

- Replace useFetchRiskAssessment with useFetchRiskAssessmentsByGroup
  calling GET /risk-assessment/group/{groupId}
- Fix RiskAssessment, RiskAssessmentResults, CriterionResult types to
  match backend response shapes (nested categories, scoring object)
- Add CategoryResult, CategoryScore, OverallScore, ScoringResult types
- Rename ProductRiskAssessment prop from riskAssessmentId to groupId
- Add group-to-assessment lookup flow: fetch by group, derive ID
- Fix download endpoint to use /document/{category} path
- Handle empty assessment state with StateNoData + create button

Implements JIRAPLAY-1396

Assisted-by: Claude Code
@mrizzi
Copy link
Copy Markdown
Owner Author

mrizzi commented Apr 2, 2026

Verification Report for JIRAPLAY-1380 (commit abab1dc)

Check Result Details
Review Feedback PASS All 5 threads classified in prior run; no new unclassified threads
Root-Cause Investigation N/A Prior root-cause tasks JIRAPLAY-1395 and JIRAPLAY-1397 already exist
Scope Containment PASS 5 files match task scope (including sub-task JIRAPLAY-1396 scope)
Diff Size PASS 406 additions, 5 deletions across 5 files — appropriate for scope
Commit Traceability PASS All 3 commits reference Jira issues (JIRAPLAY-1380, -1394, -1396)
Sensitive Patterns PASS No secrets or credentials detected
CI Status PASS checks, ci, coverage all pass; Sourcery review pass
Acceptance Criteria PASS 7 of 7 criteria met; verified against running backend
Verification Commands PASS Build compiles (pre-existing errors only), Biome check passes

Overall: PASS

All checks pass. The PR includes the original implementation (JIRAPLAY-1380), the window.location.reload() fix (JIRAPLAY-1394), and the backend API alignment fix (JIRAPLAY-1396). Verified end-to-end against a running trustify backend with both empty-assessment and existing-assessment flows working correctly.


This comment was AI-generated by sdlc-workflow/verify-pr v0.5.7.

mrizzi added 8 commits April 2, 2026 19:15
- Render assessment results inside the wizard layout (left nav + right
  panel) instead of replacing the wizard entirely
- Add "Download Assessment" button alongside "Start New Assessment"
- Wrap criteria table in a Card with "Criteria Summary" title
- Replace Label badges with icon + text for risk level column
  (ExclamationCircleIcon for high, ExclamationTriangleIcon for moderate,
  CheckCircleIcon for low)
- Reposition buttons below score percentage and submission date
- Keep wizard footer (Back/Next) visible in results view

Implements JIRAPLAY-1406

Assisted-by: Claude Code
…ate in results view

Remove !!resultsContent guards from isActive and isDisabled props in the
assessment wizard. The first nav item (SAR) is now visually highlighted
when viewing results, and Back/Next buttons follow their normal disabled
logic instead of being unconditionally disabled.

Implements JIRAPLAY-1407

Assisted-by: Claude Code
Call queryClient.invalidateQueries on RiskAssessmentsQueryKey after a
successful document upload so the parent component re-fetches the
assessment list and automatically switches from wizard to results view
when the backend sets status to 'completed'.

Implements JIRAPLAY-1409

Assisted-by: Claude Code
Replace plain text number prefix ('1. Category') with circular badge
indicators matching the Figma mockup. Active step shows filled primary
color circle with white text, inactive steps show outlined circles with
neutral border. Completed steps retain the green CheckCircleIcon.

Implements JIRAPLAY-1411

Assisted-by: Claude Code
The backend returns assessments sorted by createdAt ascending (oldest
first). Change from assessments[0] to assessments[assessments.length - 1]
so the most recent assessment determines the view state (wizard vs
results).

Implements JIRAPLAY-1413

Assisted-by: Claude Code
Replace global resultsContent overlay with per-category rendering:
- Each wizard step independently shows upload area or category results
  based on CategoryResult.processed from the API
- Navigation (Next/Back/nav clicks) always works, switching between
  categories with different content per step
- Derive completed steps from API data (processed boolean) merged
  with local upload tracking
- Refactor AssessmentResults to AssessmentCategoryResults accepting
  per-category data via props instead of fetching globally
- Add useDeleteRiskAssessmentMutation for Start New Assessment flow
  (delete old + create new instead of orphaning duplicates)
- Show per-category score and criteria, not flattened across all

Implements JIRAPLAY-1415

Assisted-by: Claude Code
The PatternFly token --pf-t--global--color--nonstatus--white--default
does not resolve to white in PF6. Use explicit #fff for the active
step number text color.

Implements JIRAPLAY-1415

Assisted-by: Claude Code
Add formatCriterionLabel() to convert snake_case backend keys
(e.g., threat_identification) into readable labels (Threat
identification). Applied to criterion names, completeness values,
and risk level text. Falls back gracefully for unknown keys.

Implements JIRAPLAY-1418

Assisted-by: Claude Code
@mrizzi
Copy link
Copy Markdown
Owner Author

mrizzi commented Apr 7, 2026

Verification Report for JIRAPLAY-1426 (commit 1c6172e)

Check Result Details
Review Feedback PASS 5 threads found, all already classified from prior run. No new unclassified threads.
Root-Cause Investigation N/A No new sub-tasks created
Scope Containment WARN 2/2 expected files present; 4 out-of-scope files from sibling tasks sharing this PR
Diff Size WARN 559+/36- across 6 files — PR covers multiple tasks (JIRAPLAY-1380, 1394, 1396, 1406, 1407, 1409, 1411, 1413, 1415, 1418)
Commit Traceability FAIL 0/11 commits reference JIRAPLAY-1426 — task has not been implemented yet
Sensitive Patterns PASS No sensitive patterns detected
CI Status PASS checks ✓, ci ✓, coverage ✓, Sourcery ✓
Acceptance Criteria FAIL 1/3 criteria met
Verification Commands N/A No verification commands defined

Acceptance Criteria Detail

# Criterion Result
1 Download button triggers GET /v2/risk-assessment/{id}/report FAIL — currently calls /document/${category}
2 Downloaded file has .pdf extension and includes assessment ID FAIL — downloads raw document, no .pdf extension
3 No TypeScript build errors (npm run build succeeds) PASS — CI passes

Overall: FAIL

JIRAPLAY-1426 has not been implemented yet. The task is in "New" status and no commits on this PR reference it. The download hook (useDownloadAssessmentDocument) still calls the raw document endpoint (/document/${category}) instead of the report generation endpoint (/report). Run /implement-task JIRAPLAY-1426 to implement the frontend changes.


This comment was AI-generated by sdlc-workflow/verify-pr v0.5.10.

Replace useDownloadAssessmentDocument (which downloads the raw uploaded
PDF via /document/{category}) with useDownloadAssessmentReport (which
calls GET /risk-assessment/{id}/report to download the generated PDF
report). Remove the category parameter since the report covers all
categories.

Implements JIRAPLAY-1426

Assisted-by: Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant