Skip to content

feat: limit badges to 2 with hover/click popover in UserListTable#8

Open
tomerqodo wants to merge 2 commits into
qodo_full_base_feat_limit_badges_to_2_with_hover_click_popover_in_userlisttable_pr8from
qodo_full_head_feat_limit_badges_to_2_with_hover_click_popover_in_userlisttable_pr8
Open

feat: limit badges to 2 with hover/click popover in UserListTable#8
tomerqodo wants to merge 2 commits into
qodo_full_base_feat_limit_badges_to_2_with_hover_click_popover_in_userlisttable_pr8from
qodo_full_head_feat_limit_badges_to_2_with_hover_click_popover_in_userlisttable_pr8

Conversation

@tomerqodo

Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#8

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. handleDownload throws generic Error 📘 Rule violation ⛯ Reliability
Description
handleDownload uses multiple throw new Error(...) statements for expected failure modes
  (missing org fields, missing headers, empty results, CSV generation failure).
• This violates the requirement to use hierarchical custom error classes with typed codes and (where
  applicable) HTTP status codes, reducing debuggability and consistent handling.
• It also makes it harder to programmatically differentiate user-correctable issues vs system
  failures.
Code

apps/web/modules/users/components/UserTable/UserListTable.tsx[R581-584]

+  const handleDownload = async (): Promise<void> => {
    try {
      if (!org?.slug || !org?.name) {
        throw new Error("Org slug or name is missing.");
Evidence
PR Compliance ID 15 forbids throwing generic Error objects and requires structured, typed custom
errors. The new handleDownload implementation throws multiple generic Error instances for
various conditions.

AGENTS.md
apps/web/modules/users/components/UserTable/UserListTable.tsx[581-622]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`handleDownload` throws generic `Error` objects for multiple failure cases. Compliance requires hierarchical custom errors with typed codes (and status codes for HTTP/API errors) plus structured context.

## Issue Context
This function covers a CSV export flow with several expected validation/failure branches (missing org data, missing headers, empty result set, CSV generation failure). These should be represented with typed custom errors to enable consistent handling and debugging.

## Fix Focus Areas
- apps/web/modules/users/components/UserTable/UserListTable.tsx[581-622]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. showToast renders raw error 📘 Rule violation ⛨ Security
Description
• The catch block shows the raw caught error value to the end-user via showToast(\Error:
  ${error}\, "error"), which may expose internal implementation details (e.g., stack traces or
  serialized objects).
• Secure error handling requires user-facing messages to be generic, while detailed error
  information is written only to internal logs.
• This also risks poor UX because interpolating an Error can produce inconsistent output (e.g.,
  Error: [object Object]).
Code

↗ apps/web/modules/users/components/UserTable/UserListTable.tsx

            <p className="text-brand-subtle shrink-0 px-2 text-center text-xs leading-none sm:text-sm sm:font-medium">
Evidence
PR Compliance ID 4 requires generic user-facing errors and prohibits exposing internal details. The
new code directly interpolates the caught error into a toast message shown to users.

Rule 4: Generic: Secure Error Handling
apps/web/modules/users/components/UserTable/UserListTable.tsx[627-631]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The UI toast displays the raw caught `error` value, which can leak internal details to end-users.

## Issue Context
Compliance requires generic user-facing messages (e.g., &quot;An internal error occurred&quot;) and detailed diagnostics only in internal logs.

## Fix Focus Areas
- apps/web/modules/users/components/UserTable/UserListTable.tsx[627-631]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Hidden items not clickable 🐞 Bug ✓ Correctness
Description
• LimitedBadges accepts per-item onClick, but renders overflow (hidden) items as plain <span>
  without binding item.onClick, so clicks do nothing.
• UserListTable passes onClick handlers for team badges to filter the table; for users in >2 teams,
  hidden teams in the popover cannot be clicked to filter.
• Hidden items are styled with cursor-pointer, increasing user confusion because they look
  interactive but aren’t.
Code

apps/web/components/ui/LimitedBadges.tsx[R91-98]

+            <div className="flex flex-col gap-1">
+              {hiddenItems.map((item, index) => (
+                <span
+                  key={item.label}
+                  className="text-default cursor-pointer text-sm hover:text-emphasis">
+                  {item.label}
+                </span>
+              ))}
Evidence
LimitedBadges only wires onClick for visible badges, but not for hidden items. UserListTable
provides onClick handlers for teams expecting filtering to occur; those handlers will never fire for
overflow items.

apps/web/components/ui/LimitedBadges.tsx[66-98]
apps/web/modules/users/components/UserTable/UserListTable.tsx[367-394]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Hidden (overflow) items in `LimitedBadges` are rendered as non-interactive `&lt;span&gt;` elements. This ignores `BadgeItem.onClick`, causing functional regressions (e.g., team filtering in `UserListTable` for users with &gt;2 teams).

### Issue Context
`UserListTable` passes `onClick` handlers to `LimitedBadges` items (teams column) expecting filter changes when clicking team names. Only the first `maxVisible` items are actually clickable today.

### Fix Focus Areas
- apps/web/components/ui/LimitedBadges.tsx[66-101]
- apps/web/modules/users/components/UserTable/UserListTable.tsx[385-393]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Label-based key collisions 🐞 Bug ⛯ Reliability
Description
• LimitedBadges uses item.label as the React key for both visible and hidden lists; labels are not
  guaranteed unique.
• In insights, multi-select values are typed as an arbitrary string array (no uniqueness
  constraint), and labels are derived from optionMap, so duplicate keys can occur.
• Duplicate keys can cause React reconciliation bugs (wrong item reused) and runtime warnings,
  especially in dense table UIs.
Code

apps/web/components/ui/LimitedBadges.tsx[R68-72]

+      {visibleItems.map((item, index) => (
+        <Badge key={item.label} variant={item.variant || "gray"} onClick={item.onClick}>
+          {item.label}
+        </Badge>
+      ))}
Evidence
LimitedBadges keys are derived from labels. ResponseValueCell derives labels from optionMap and the
multi-value schema permits duplicate strings, so duplicate labels/values are possible within a
single rendered list.

apps/web/components/ui/LimitedBadges.tsx[68-72]
apps/web/modules/insights/components/ResponseValueCell.tsx[15-21]
apps/web/modules/insights/hooks/useInsightsColumns.tsx[173-179]
packages/features/insights/lib/types.ts[9-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`LimitedBadges` uses `item.label` as the React list key. Labels are not guaranteed unique (and multi-select values are not unique-enforced), which can produce duplicate keys and reconciliation issues.

### Issue Context
- Response values are validated as `z.array(z.string())` (no uniqueness constraint).
- `ResponseValueCell` labels are derived via `optionMap[id] ?? id`, and `optionMap` construction does not enforce unique labels.

### Fix Focus Areas
- apps/web/components/ui/LimitedBadges.tsx[11-26]
- apps/web/components/ui/LimitedBadges.tsx[66-98]
- apps/web/modules/insights/components/ResponseValueCell.tsx[15-21]
- apps/web/modules/users/components/UserTable/UserListTable.tsx[239-266]
- apps/web/modules/users/components/UserTable/UserListTable.tsx[385-393]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



ⓘ The new review experience is currently in Beta. Learn more

Qodo Logo

Comment on lines +581 to 584
const handleDownload = async (): Promise<void> => {
try {
if (!org?.slug || !org?.name) {
throw new Error("Org slug or name is missing.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. handledownload throws generic error 📘 Rule violation ⛯ Reliability

handleDownload uses multiple throw new Error(...) statements for expected failure modes
  (missing org fields, missing headers, empty results, CSV generation failure).
• This violates the requirement to use hierarchical custom error classes with typed codes and (where
  applicable) HTTP status codes, reducing debuggability and consistent handling.
• It also makes it harder to programmatically differentiate user-correctable issues vs system
  failures.
Agent prompt
## Issue description
`handleDownload` throws generic `Error` objects for multiple failure cases. Compliance requires hierarchical custom errors with typed codes (and status codes for HTTP/API errors) plus structured context.

## Issue Context
This function covers a CSV export flow with several expected validation/failure branches (missing org data, missing headers, empty result set, CSV generation failure). These should be represented with typed custom errors to enable consistent handling and debugging.

## Fix Focus Areas
- apps/web/modules/users/components/UserTable/UserListTable.tsx[581-622]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +91 to +98
<div className="flex flex-col gap-1">
{hiddenItems.map((item, index) => (
<span
key={item.label}
className="text-default cursor-pointer text-sm hover:text-emphasis">
{item.label}
</span>
))}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Hidden items not clickable 🐞 Bug ✓ Correctness

• LimitedBadges accepts per-item onClick, but renders overflow (hidden) items as plain <span>
  without binding item.onClick, so clicks do nothing.
• UserListTable passes onClick handlers for team badges to filter the table; for users in >2 teams,
  hidden teams in the popover cannot be clicked to filter.
• Hidden items are styled with cursor-pointer, increasing user confusion because they look
  interactive but aren’t.
Agent prompt
### Issue description
Hidden (overflow) items in `LimitedBadges` are rendered as non-interactive `<span>` elements. This ignores `BadgeItem.onClick`, causing functional regressions (e.g., team filtering in `UserListTable` for users with >2 teams).

### Issue Context
`UserListTable` passes `onClick` handlers to `LimitedBadges` items (teams column) expecting filter changes when clicking team names. Only the first `maxVisible` items are actually clickable today.

### Fix Focus Areas
- apps/web/components/ui/LimitedBadges.tsx[66-101]
- apps/web/modules/users/components/UserTable/UserListTable.tsx[385-393]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +68 to +72
{visibleItems.map((item, index) => (
<Badge key={item.label} variant={item.variant || "gray"} onClick={item.onClick}>
{item.label}
</Badge>
))}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

4. Label-based key collisions 🐞 Bug ⛯ Reliability

• LimitedBadges uses item.label as the React key for both visible and hidden lists; labels are not
  guaranteed unique.
• In insights, multi-select values are typed as an arbitrary string array (no uniqueness
  constraint), and labels are derived from optionMap, so duplicate keys can occur.
• Duplicate keys can cause React reconciliation bugs (wrong item reused) and runtime warnings,
  especially in dense table UIs.
Agent prompt
### Issue description
`LimitedBadges` uses `item.label` as the React list key. Labels are not guaranteed unique (and multi-select values are not unique-enforced), which can produce duplicate keys and reconciliation issues.

### Issue Context
- Response values are validated as `z.array(z.string())` (no uniqueness constraint).
- `ResponseValueCell` labels are derived via `optionMap[id] ?? id`, and `optionMap` construction does not enforce unique labels.

### Fix Focus Areas
- apps/web/components/ui/LimitedBadges.tsx[11-26]
- apps/web/components/ui/LimitedBadges.tsx[66-98]
- apps/web/modules/insights/components/ResponseValueCell.tsx[15-21]
- apps/web/modules/users/components/UserTable/UserListTable.tsx[239-266]
- apps/web/modules/users/components/UserTable/UserListTable.tsx[385-393]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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