Skip to content

Assessments evidence tray#7560

Merged
lucanovera merged 17 commits intomainfrom
ENG-2748-UI-Evidence-tray
Mar 6, 2026
Merged

Assessments evidence tray#7560
lucanovera merged 17 commits intomainfrom
ENG-2748-UI-Evidence-tray

Conversation

@lucanovera
Copy link
Contributor

@lucanovera lucanovera commented Mar 4, 2026

Ticket ENG-2748

Description Of Changes

Adds an evidence drawer to the privacy assessments detail page, allowing users to view the complete evidence trail for any question group. Clicking "View evidence" on a question group panel opens a right-side drawer showing all evidence items collected for that section, organized into "System-derived data" and "Human input" categories using collapsible panels.

Evidence items are deduplicated (same source/field/value across multiple questions are shown once) and filterable via a search bar that matches against evidence values, source type labels, and field name labels.

Note: I have only been able to test system data, team data will be tested with Slack once all PRs are merged.

Code Changes

  • clients/admin-ui/src/features/privacy-assessments/AssessmentDetail.tsx - Wires up drawer open/close state, focused group tracking, evidence deduplication, and search filtering; renders EvidenceDrawer
  • clients/admin-ui/src/features/privacy-assessments/EvidenceDrawer.tsx - New right-side Ant Design Drawer with search bar and EvidenceSection content
  • clients/admin-ui/src/features/privacy-assessments/EvidenceDrawer.module.scss - Drawer layout styles (sticky search bar, scrollable body, layout background)
  • clients/admin-ui/src/features/privacy-assessments/EvidenceSection.tsx - New component rendering a question group's evidence split into system-derived and human-input collapse panels; shows empty/no-match states
  • clients/admin-ui/src/features/privacy-assessments/EvidenceSection.module.scss - Collapse padding overrides
  • clients/admin-ui/src/features/privacy-assessments/EvidenceCardGroup.tsx - New component rendering a list of flat EvidenceItem cards with source type, field name, value, and date
  • clients/admin-ui/src/features/privacy-assessments/EvidenceCardGroup.module.scss - Card layout styles
  • clients/admin-ui/src/features/privacy-assessments/QuestionGroupPanel.tsx - Enables the "View evidence" button (was disabled); uses answered_count/total_count from the group directly instead of computing from question answer text
  • clients/admin-ui/src/features/privacy-assessments/types.ts - Simplifies EvidenceItem to a flat interface matching the API; adds answered_count/total_count to QuestionGroup; removes the complex discriminated union and associated sub-types
  • clients/admin-ui/src/features/privacy-assessments/utils.ts - Adds deduplicateEvidence and filterEvidence pure helpers
  • clients/admin-ui/src/features/privacy-assessments/constants.ts - Adds SOURCE_TYPE_LABELS and FIELD_NAME_LABELS maps for display formatting
  • clients/admin-ui/src/features/privacy-assessments/index.ts - Exports new EvidenceDrawer and EvidenceSection components

Steps to Confirm

  1. Navigate to an existing privacy assessment detail page
  2. Expand a question group that has evidence collected
  3. Click "View evidence" on the question group panel — a drawer should open on the right side titled "Evidence"
  4. Verify evidence items appear grouped under "System-derived data" and/or "Human input" collapsible sections with item counts
  5. Type in the search bar — evidence items should filter in real time; the "View evidence" button click should not collapse/expand the accordion (event propagation is stopped)
  6. Clear the search — all evidence should return
  7. If a group has no evidence, the drawer should show "No evidence collected yet for this section."
  8. If a search yields no results, the drawer should show a message referencing the search query
  9. Close the drawer — search query should be cleared so re-opening is fresh

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • All UX related changes have been reviewed by a designer
    • No UX review needed
  • Followup issues:
    • Followup issues created
    • No followup issues
  • Database migrations:
    • No migrations
  • Documentation:
    • No documentation updates required

Summary by CodeRabbit

  • New Features

    • Searchable evidence drawer for privacy assessments with filtered, deduplicated results
    • Evidence grouped into system-derived and human-entered sections with item counts
    • "View evidence" enabled from question groups; per-evidence cards show source, field, value, and date
    • Completed assessments display a formatted completion date and a dedicated View action
  • Style

    • Improved drawer, section and card styling for clearer layout and readability

@vercel
Copy link
Contributor

vercel bot commented Mar 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fides-plus-nightly Ready Ready Preview, Comment Mar 5, 2026 11:01pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
fides-privacy-center Ignored Ignored Mar 5, 2026 11:01pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

📝 Walkthrough

Walkthrough

Adds an evidence drawer feature to privacy assessments: new UI components (drawer, section, card group), drawer state and search in AssessmentDetail, evidence deduplication/filtering utilities, label constants, and simplified EvidenceItem types.

Changes

Cohort / File(s) Summary
Evidence UI components & styles
clients/admin-ui/src/features/privacy-assessments/EvidenceDrawer.tsx, clients/admin-ui/src/features/privacy-assessments/EvidenceDrawer.module.scss, clients/admin-ui/src/features/privacy-assessments/EvidenceSection.tsx, clients/admin-ui/src/features/privacy-assessments/EvidenceSection.module.scss, clients/admin-ui/src/features/privacy-assessments/EvidenceCardGroup.tsx, clients/admin-ui/src/features/privacy-assessments/EvidenceCardGroup.module.scss
New EvidenceDrawer, EvidenceSection, and EvidenceCardGroup components plus SCSS modules; drawer includes search and renders collapsible, categorized evidence groups.
Integration & state
clients/admin-ui/src/features/privacy-assessments/AssessmentDetail.tsx, clients/admin-ui/src/features/privacy-assessments/QuestionGroupPanel.tsx
AssessmentDetail: adds drawerOpen, focusedGroupId, evidenceSearchQuery, dedupe/filter flow, and handlers to open/close drawer; QuestionGroupPanel exposes onViewEvidence and wires the view button.
Types & API surface
clients/admin-ui/src/features/privacy-assessments/types.ts, clients/admin-ui/src/features/privacy-assessments/index.ts
Collapsed multiple specialized evidence interfaces into a single EvidenceItem, changed AssessmentEvidenceResponse.by_type to Record<EvidenceType, EvidenceItem[]>, added answered_count and total_count to QuestionGroup, and re-exported new components.
Utilities & constants
clients/admin-ui/src/features/privacy-assessments/utils.ts, clients/admin-ui/src/features/privacy-assessments/constants.ts
Added deduplicateEvidence and filterEvidence utilities; added SOURCE_TYPE_LABELS and FIELD_NAME_LABELS mappings used for display and filtering.
Minor UI adjustments
clients/admin-ui/src/features/privacy-assessments/AssessmentCard.tsx
Adjusted completed assessment layout to show formatted completion date and reorganized avatar/text/View button layout.
Changelog
changelog/7560-evidence-drawer.yaml
New changelog entry documenting the evidence drawer feature.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant AssessmentDetail as "AssessmentDetail"
    participant EvidenceDrawer as "EvidenceDrawer"
    participant EvidenceSection as "EvidenceSection"
    participant EvidenceCardGroup as "EvidenceCardGroup"

    User->>AssessmentDetail: Click "View evidence"
    AssessmentDetail->>AssessmentDetail: handleViewEvidence() sets drawerOpen, focusedGroupId
    AssessmentDetail->>EvidenceDrawer: render(open=true, group, evidence, searchQuery)
    EvidenceDrawer->>EvidenceSection: pass group, evidence, searchQuery
    EvidenceSection->>EvidenceSection: categorize (system vs human), build collapse items
    EvidenceSection->>EvidenceCardGroup: render groups with items
    EvidenceCardGroup->>EvidenceCardGroup: render individual cards

    User->>EvidenceDrawer: type search query
    EvidenceDrawer->>AssessmentDetail: onSearchChange(query)
    AssessmentDetail->>AssessmentDetail: dedupe + filterEvidence(query)
    AssessmentDetail->>EvidenceDrawer: re-render with filtered evidence
    EvidenceDrawer->>EvidenceSection: update evidence prop

    User->>EvidenceDrawer: close drawer
    EvidenceDrawer->>AssessmentDetail: onClose()
    AssessmentDetail->>AssessmentDetail: handleCloseDrawer() clears state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nrxsmith

Poem

🐰 I dug a little drawer in code,
Cards and labels in tidy rows.
Search and dedupe, hop and peep,
Evidence neat for reviewers to keep.
🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Assessments evidence tray' is directly related to the PR's main objective of adding an evidence drawer/tray to privacy assessments, clearly summarizing the primary change.
Description check ✅ Passed The PR description comprehensively covers all required template sections: ticket reference, detailed description of changes, comprehensive code changes list, clear steps to confirm, and completed pre-merge checklist with all applicable items addressed.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 ENG-2748-UI-Evidence-tray

Comment @coderabbitai help to get the list of available commands and usage tips.

@lucanovera lucanovera marked this pull request as ready for review March 5, 2026 20:26
@lucanovera lucanovera requested a review from a team as a code owner March 5, 2026 20:26
@lucanovera lucanovera requested review from jpople and kruulik and removed request for a team and jpople March 5, 2026 20:26
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 5, 2026

Greptile Summary

This PR adds an evidence drawer to the privacy assessments detail page, allowing users to view the complete evidence trail for any question group. Clicking "View evidence" on an expanded group panel opens a 600px right-side Ant Design Drawer showing deduplicated evidence items organized into "System-derived data" and "Human input" collapsible sections, with real-time search filtering against evidence values, source type labels, and field name labels. The EvidenceItem type is also simplified from a complex discriminated union to a flat interface matching the new API shape, and QuestionGroup gains answered_count/total_count fields sourced directly from the API rather than computed client-side.

Key changes:

  • New components: EvidenceDrawer, EvidenceSection, EvidenceCardGroup — well-structured and composable
  • New utilities: deduplicateEvidence and filterEvidence pure helpers in utils.ts
  • Display label maps: SOURCE_TYPE_LABELS and FIELD_NAME_LABELS constants added
  • Style concern: Multiple bare div elements are used across EvidenceDrawer, EvidenceSection, and EvidenceCardGroup where Ant Design Flex/Space or semantic HTML elements would be more appropriate per project conventions
  • Search state: Custom useState is used for the evidence search query instead of the existing useSearch hook (which supports disableUrlState: true for ephemeral, non-URL-synced search — exactly this use case)

Confidence Score: 4/5

  • This PR is safe to merge with minor style improvements recommended.
  • The feature logic is correct — deduplication, filtering, and drawer open/close flows all work as described. No runtime errors or data integrity issues were found. The two flagged items (div usage and custom search state vs useSearch hook) are style/convention concerns that do not affect functionality, lowering the score slightly from 5.
  • EvidenceDrawer.tsx and EvidenceSection.tsx for div usage; AssessmentDetail.tsx for custom search state management.

Important Files Changed

Filename Overview
clients/admin-ui/src/features/privacy-assessments/AssessmentDetail.tsx Adds drawer open/close state, focused group tracking, evidence deduplication, and search filtering; wires up EvidenceDrawer. Uses custom useState for search instead of the existing useSearch hook (see inline comment).
clients/admin-ui/src/features/privacy-assessments/EvidenceDrawer.tsx New right-side Ant Design Drawer with search bar and EvidenceSection content. Several bare div elements (drawerTitle, searchBar, body) should be replaced with semantic/Ant Design alternatives per project style rules.
clients/admin-ui/src/features/privacy-assessments/EvidenceSection.tsx New component rendering system-derived and human-input evidence in collapsible panels. div wrappers inside collapse labels (lines 56, 79) should use Flex/Space instead.
clients/admin-ui/src/features/privacy-assessments/EvidenceCardGroup.tsx New component rendering a list of flat evidence item cards with source type, field name, value, and date. The card container uses a bare div; consider a semantic element like article.
clients/admin-ui/src/features/privacy-assessments/QuestionGroupPanel.tsx Enables the View evidence button and switches from locally computed answered/total counts to API-provided answered_count/total_count fields. Clean, well-targeted change.
clients/admin-ui/src/features/privacy-assessments/types.ts Simplifies EvidenceItem to a flat interface matching the API; adds answered_count/total_count to QuestionGroup; removes the complex discriminated union. Straightforward simplification with no apparent regressions.
clients/admin-ui/src/features/privacy-assessments/utils.ts Adds deduplicateEvidence and filterEvidence pure helpers. Both are straightforward and correct — deduplication uses a composite key, filtering matches against value, source type label, and field name label.
clients/admin-ui/src/features/privacy-assessments/constants.ts Adds SOURCE_TYPE_LABELS and FIELD_NAME_LABELS lookup maps for display formatting. Clean addition with no issues.
clients/admin-ui/src/features/privacy-assessments/index.ts Exports new EvidenceDrawer and EvidenceSection components. Trivial barrel file update.

Last reviewed commit: 4a59fd4

Comment on lines +31 to +38
<div className={styles.drawerTitle}>
<Title level={5} className={styles.drawerHeading}>
Evidence
</Title>
<Text type="secondary" size="sm">
Complete evidence trail organized by question
</Text>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid div — use Ant Design Space or Flex instead

The div.drawerTitle wrapper (line 31) and the div.searchBar / div.body structural sections (lines 46, 55) should use semantic or component-library alternatives rather than plain div elements where possible.

The same pattern also appears in:

  • EvidenceSection.tsx:56 and EvidenceSection.tsx:79div wrapping Flex + Text inside collapse panel labels
  • EvidenceCardGroup.tsx:16div used as the evidence card container

For the drawer title, a Space direction="vertical" size={0} or Flex vertical would remove the need for the bare div. For the collapse labels, the same Ant Design Space or Flex vertical approach applies. The card container in EvidenceCardGroup could use a semantic element such as article to better describe the content.

Suggested change
<div className={styles.drawerTitle}>
<Title level={5} className={styles.drawerHeading}>
Evidence
</Title>
<Text type="secondary" size="sm">
Complete evidence trail organized by question
</Text>
</div>
<Space direction="vertical" size={0} className={styles.drawerTitle}>
<Title level={5} className={styles.drawerHeading}>
Evidence
</Title>
<Text type="secondary" size="sm">
Complete evidence trail organized by question
</Text>
</Space>

Context Used: Rule from dashboard - Avoid using div elements when possible. Use semantic HTML elements or component library alternativ... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
const [drawerOpen, setDrawerOpen] = useState(false);
const [focusedGroupId, setFocusedGroupId] = useState<string | null>(null);
const [evidenceSearchQuery, setEvidenceSearchQuery] = useState("");
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer useSearch hook over custom useState

The evidence search is managed with a plain useState (evidenceSearchQuery) and a manual reset in handleCloseDrawer. The existing useSearch hook already handles in-memory (non-URL-synced) search state via disableUrlState: true, and also provides a resetSearch utility, which would simplify the close handler.

// Instead of:
const [evidenceSearchQuery, setEvidenceSearchQuery] = useState("");
// ...
const handleCloseDrawer = useCallback(() => {
  setDrawerOpen(false);
  setEvidenceSearchQuery(""); // manual reset
}, []);

// Prefer:
const { searchQuery: evidenceSearchQuery, updateSearch: setEvidenceSearchQuery, resetSearch: resetEvidenceSearch } = useSearch({ disableUrlState: true });
// ...
const handleCloseDrawer = useCallback(() => {
  setDrawerOpen(false);
  resetEvidenceSearch();
}, [resetEvidenceSearch]);

Context Used: Rule from dashboard - Use the existing useSearch hook for search functionality to maintain URL state synchronization ins... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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: 1

🧹 Nitpick comments (2)
clients/admin-ui/src/features/privacy-assessments/EvidenceSection.tsx (1)

104-106: Consider expanding sections by default for better discoverability.

The Collapse has no defaultActiveKey, so both sections start collapsed. Users must click to reveal evidence items. If the drawer is opened specifically to view evidence, consider expanding sections by default.

♻️ Proposed: Expand all sections by default
      <div className={styles.collapseWrapper}>
-       <Collapse items={collapseItems} ghost />
+       <Collapse
+         items={collapseItems}
+         defaultActiveKey={collapseItems.map((item) => item.key)}
+         ghost
+       />
      </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/admin-ui/src/features/privacy-assessments/EvidenceSection.tsx` around
lines 104 - 106, The Collapse sections are all collapsed because no
defaultActiveKey is provided; modify the EvidenceSection component so the
Collapse (symbol: Collapse) opens sections by default by passing a
defaultActiveKey prop that contains the keys of collapseItems (symbol:
collapseItems) — e.g., compute the keys from collapseItems and pass them as
defaultActiveKey so all sections are expanded when the drawer opens; ensure keys
match the item.key values used to build collapseItems.
clients/admin-ui/src/features/privacy-assessments/AssessmentDetail.tsx (1)

239-247: Consider passing focusedGroupId only when the drawer is open.

The focusedGroupId prop is passed regardless of whether group is defined. If EvidenceDrawer or EvidenceSection relies on groupId being non-null when group is defined, the current flow is fine. However, if focusedGroupId can be stale after closing and reopening with a new group, verify that the downstream components handle the transition gracefully.

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

In `@clients/admin-ui/src/features/privacy-assessments/AssessmentDetail.tsx`
around lines 239 - 247, The EvidenceDrawer is always receiving focusedGroupId
even when closed, which can leave a stale id active; update the prop usage in
the AssessmentDetail render so focusedGroupId is only passed when the drawer is
open (use drawerOpen to gate passing focusedGroupId, otherwise pass
undefined/null), and ensure this change targets the EvidenceDrawer prop in the
JSX where EvidenceDrawer is instantiated (symbols: EvidenceDrawer,
focusedGroupId, drawerOpen, focusedGroup).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/admin-ui/src/features/privacy-assessments/utils.ts`:
- Around line 50-63: The filter uses the untrimmed query for matching: after the
early-return check it sets lower = query.toLowerCase() but query wasn't trimmed;
change to compute lower from query.trim() (e.g., const lower =
query.trim().toLowerCase()) so items.filter comparisons (item.value,
SOURCE_TYPE_LABELS[item.source_type], FIELD_NAME_LABELS[item.field_name] and
item.field_name.replace(/_/g, " ")) use the normalized/truncated query for
matching.

---

Nitpick comments:
In `@clients/admin-ui/src/features/privacy-assessments/AssessmentDetail.tsx`:
- Around line 239-247: The EvidenceDrawer is always receiving focusedGroupId
even when closed, which can leave a stale id active; update the prop usage in
the AssessmentDetail render so focusedGroupId is only passed when the drawer is
open (use drawerOpen to gate passing focusedGroupId, otherwise pass
undefined/null), and ensure this change targets the EvidenceDrawer prop in the
JSX where EvidenceDrawer is instantiated (symbols: EvidenceDrawer,
focusedGroupId, drawerOpen, focusedGroup).

In `@clients/admin-ui/src/features/privacy-assessments/EvidenceSection.tsx`:
- Around line 104-106: The Collapse sections are all collapsed because no
defaultActiveKey is provided; modify the EvidenceSection component so the
Collapse (symbol: Collapse) opens sections by default by passing a
defaultActiveKey prop that contains the keys of collapseItems (symbol:
collapseItems) — e.g., compute the keys from collapseItems and pass them as
defaultActiveKey so all sections are expanded when the drawer opens; ensure keys
match the item.key values used to build collapseItems.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 600d47d4-8d70-4095-a80d-f0fdc58454e2

📥 Commits

Reviewing files that changed from the base of the PR and between faa52ee and 4a59fd4.

📒 Files selected for processing (13)
  • changelog/7560-evidence-drawer.yaml
  • clients/admin-ui/src/features/privacy-assessments/AssessmentDetail.tsx
  • clients/admin-ui/src/features/privacy-assessments/EvidenceCardGroup.module.scss
  • clients/admin-ui/src/features/privacy-assessments/EvidenceCardGroup.tsx
  • clients/admin-ui/src/features/privacy-assessments/EvidenceDrawer.module.scss
  • clients/admin-ui/src/features/privacy-assessments/EvidenceDrawer.tsx
  • clients/admin-ui/src/features/privacy-assessments/EvidenceSection.module.scss
  • clients/admin-ui/src/features/privacy-assessments/EvidenceSection.tsx
  • clients/admin-ui/src/features/privacy-assessments/QuestionGroupPanel.tsx
  • clients/admin-ui/src/features/privacy-assessments/constants.ts
  • clients/admin-ui/src/features/privacy-assessments/index.ts
  • clients/admin-ui/src/features/privacy-assessments/types.ts
  • clients/admin-ui/src/features/privacy-assessments/utils.ts

Comment on lines +50 to +63
if (!query.trim()) {
return items;
}
const lower = query.toLowerCase();
return items.filter(
(item) =>
item.value.toLowerCase().includes(lower) ||
(SOURCE_TYPE_LABELS[item.source_type] ?? item.source_type)
.toLowerCase()
.includes(lower) ||
(FIELD_NAME_LABELS[item.field_name] ?? item.field_name.replace(/_/g, " "))
.toLowerCase()
.includes(lower),
);
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

Normalize the trimmed query before filtering.

Line 50 trims only for emptiness, but Line 53 uses the untrimmed query for matching. Inputs like " policy " won’t match expected results.

Proposed fix
 export const filterEvidence = (
   items: EvidenceItem[],
   query: string,
 ): EvidenceItem[] => {
-  if (!query.trim()) {
+  const normalizedQuery = query.trim().toLowerCase();
+  if (!normalizedQuery) {
     return items;
   }
-  const lower = query.toLowerCase();
   return items.filter(
     (item) =>
-      item.value.toLowerCase().includes(lower) ||
+      item.value.toLowerCase().includes(normalizedQuery) ||
       (SOURCE_TYPE_LABELS[item.source_type] ?? item.source_type)
         .toLowerCase()
-        .includes(lower) ||
+        .includes(normalizedQuery) ||
       (FIELD_NAME_LABELS[item.field_name] ?? item.field_name.replace(/_/g, " "))
         .toLowerCase()
-        .includes(lower),
+        .includes(normalizedQuery),
   );
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/admin-ui/src/features/privacy-assessments/utils.ts` around lines 50 -
63, The filter uses the untrimmed query for matching: after the early-return
check it sets lower = query.toLowerCase() but query wasn't trimmed; change to
compute lower from query.trim() (e.g., const lower = query.trim().toLowerCase())
so items.filter comparisons (item.value, SOURCE_TYPE_LABELS[item.source_type],
FIELD_NAME_LABELS[item.field_name] and item.field_name.replace(/_/g, " ")) use
the normalized/truncated query for matching.

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: 1

🧹 Nitpick comments (1)
clients/admin-ui/src/features/privacy-assessments/AssessmentCard.tsx (1)

127-129: Add contextual accessible label to the “View” button.

“View” is generic in assistive-tech button lists. Add an aria-label including the assessment name.

Suggested tweak
-              <Button type="link" className="p-0" onClick={onClick}>
+              <Button
+                type="link"
+                className="p-0"
+                onClick={onClick}
+                aria-label={`View assessment ${assessment.name}`}
+              >
                 View
               </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/admin-ui/src/features/privacy-assessments/AssessmentCard.tsx` around
lines 127 - 129, The "View" Button in AssessmentCard.tsx is too generic for
screen-reader users; update the Button (inside the AssessmentCard component) to
include an aria-label that incorporates the assessment's name/title (e.g., use
the assessment.name or assessment.title prop/state used in this component) so
each button reads something like "View assessment {name}" and preserve the
existing onClick handler and className. Ensure the aria-label value is derived
from the same identifier used elsewhere in the card to avoid mismatches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/admin-ui/src/features/privacy-assessments/AssessmentCard.tsx`:
- Around line 62-65: The component's completion logic (completionDate) and the
completion badge render in AssessmentCard incorrectly treat assessments with
completeness === 100 as completed even when status ===
AssessmentStatus.OUTDATED; update the logic in the Completion computation and
the area that renders the completion badge/text (references: completionDate,
isComplete, assessment.updated_at, and the rendering block that shows
"Assessment complete") to first check for assessment.status ===
AssessmentStatus.OUTDATED and, if outdated, use the statusLabel (or render the
outdated label/badge) instead of the completed text/date so outdated assessments
are never shown as "Completed".

---

Nitpick comments:
In `@clients/admin-ui/src/features/privacy-assessments/AssessmentCard.tsx`:
- Around line 127-129: The "View" Button in AssessmentCard.tsx is too generic
for screen-reader users; update the Button (inside the AssessmentCard component)
to include an aria-label that incorporates the assessment's name/title (e.g.,
use the assessment.name or assessment.title prop/state used in this component)
so each button reads something like "View assessment {name}" and preserve the
existing onClick handler and className. Ensure the aria-label value is derived
from the same identifier used elsewhere in the card to avoid mismatches.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f09f3282-999c-4cf3-a817-1e4b7a7fac57

📥 Commits

Reviewing files that changed from the base of the PR and between 4a59fd4 and d7459fb.

📒 Files selected for processing (1)
  • clients/admin-ui/src/features/privacy-assessments/AssessmentCard.tsx

Comment on lines +62 to +65
const completionDate =
isComplete && assessment.updated_at
? `Completed on ${formatDate(assessment.updated_at, { showTime: false })}`
: statusLabel;
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

Outdated assessments can be mislabeled as completed.

For AssessmentStatus.OUTDATED with completeness === 100, Line 64 renders “Completed on …” and Lines 119-121 still show “Assessment complete,” which hides the outdated state.

Proposed fix
   const riskLabel = riskLevel ? RISK_LEVEL_LABELS[riskLevel] : "N/A";
   const statusLabel = status ? ASSESSMENT_STATUS_LABELS[status] : "N/A";
   const isComplete = completeness === 100;
+  const isOutdated = status === AssessmentStatus.OUTDATED;
   const completionDate =
-    isComplete && assessment.updated_at
+    isOutdated
+      ? statusLabel
+      : isComplete && assessment.updated_at
       ? `Completed on ${formatDate(assessment.updated_at, { showTime: false })}`
       : statusLabel;
@@
-                  <Text strong type="success" size="sm">
-                    Assessment complete
-                  </Text>
+                  <Text strong type={isOutdated ? "danger" : "success"} size="sm">
+                    {isOutdated ? "Assessment outdated" : "Assessment complete"}
+                  </Text>

Also applies to: 119-124

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

In `@clients/admin-ui/src/features/privacy-assessments/AssessmentCard.tsx` around
lines 62 - 65, The component's completion logic (completionDate) and the
completion badge render in AssessmentCard incorrectly treat assessments with
completeness === 100 as completed even when status ===
AssessmentStatus.OUTDATED; update the logic in the Completion computation and
the area that renders the completion badge/text (references: completionDate,
isComplete, assessment.updated_at, and the rendering block that shows
"Assessment complete") to first check for assessment.status ===
AssessmentStatus.OUTDATED and, if outdated, use the statusLabel (or render the
outdated label/badge) instead of the completed text/date so outdated assessments
are never shown as "Completed".

Copy link
Contributor

@kruulik kruulik left a comment

Choose a reason for hiding this comment

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

LGTM 👍

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.

🧹 Nitpick comments (1)
clients/admin-ui/src/features/privacy-assessments/types.ts (1)

220-220: Use Partial<Record<>> for by_type to accurately represent sparse API responses.

At Line 220, Record<EvidenceType, EvidenceItem[]> requires all 4 enum keys to exist whenever by_type is present. In practice, grouped responses contain only a subset of evidence types, making the current type overly strict and potentially causing type-checking friction when accessing the property.

Proposed change
-  by_type?: Record<EvidenceType, EvidenceItem[]>;
+  by_type?: Partial<Record<EvidenceType, EvidenceItem[]>>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/admin-ui/src/features/privacy-assessments/types.ts` at line 220,
Update the optional by_type property to allow sparse keys by changing its type
from Record<EvidenceType, EvidenceItem[]> to Partial<Record<EvidenceType,
EvidenceItem[]>>; locate the by_type declaration in the same module (the symbol
is by_type and the related types are EvidenceType and EvidenceItem) and replace
the type accordingly, and then update any call sites that assumed every
EvidenceType key exists to handle possibly undefined entries (e.g., check for
undefined or use optional chaining/defaults).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@clients/admin-ui/src/features/privacy-assessments/types.ts`:
- Line 220: Update the optional by_type property to allow sparse keys by
changing its type from Record<EvidenceType, EvidenceItem[]> to
Partial<Record<EvidenceType, EvidenceItem[]>>; locate the by_type declaration in
the same module (the symbol is by_type and the related types are EvidenceType
and EvidenceItem) and replace the type accordingly, and then update any call
sites that assumed every EvidenceType key exists to handle possibly undefined
entries (e.g., check for undefined or use optional chaining/defaults).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 88a3e952-513b-4436-8ae1-e7319a0a241e

📥 Commits

Reviewing files that changed from the base of the PR and between d7459fb and d3b879a.

📒 Files selected for processing (2)
  • clients/admin-ui/src/features/privacy-assessments/constants.ts
  • clients/admin-ui/src/features/privacy-assessments/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • clients/admin-ui/src/features/privacy-assessments/constants.ts

@lucanovera lucanovera added this pull request to the merge queue Mar 6, 2026
Merged via the queue into main with commit 8f2e7e4 Mar 6, 2026
47 checks passed
@lucanovera lucanovera deleted the ENG-2748-UI-Evidence-tray branch March 6, 2026 13:42
mfbrown pushed a commit that referenced this pull request Mar 12, 2026
Co-authored-by: Karolis Krulis <karolis@ethyca.com>
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.

2 participants