Skip to content

Fix Project Metrics dashboard responsiveness on small screens#3412

Merged
kasya merged 13 commits intoOWASP:mainfrom
anurag2787:fix/responsive-project-matrics
Jan 25, 2026
Merged

Fix Project Metrics dashboard responsiveness on small screens#3412
kasya merged 13 commits intoOWASP:mainfrom
anurag2787:fix/responsive-project-matrics

Conversation

@anurag2787
Copy link
Contributor

@anurag2787 anurag2787 commented Jan 18, 2026

Proposed change

This PR improves the responsiveness of the Project Metrics dashboard on smaller screens. It updates the layout to adapt properly on mobile by replacing table-style views with responsive cards, improving spacing and readability, and adding a mobile-friendly sorting control.

Resolves #3368

Before

image

After

image

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

Summary by CodeRabbit

  • New Features

    • Added a responsive "Sort By" dropdown with visual indicators and a simplified, mobile-friendly header layout; retained filter controls.
  • Style

    • Redesigned metrics card to a stacked, responsive layout.
    • Introduced colored "Score: X" badges, updated typography/padding, and improved dark-mode text styling (including dropdown label color).
  • Tests

    • Updated tests to use more robust element selection and reflect the revised UI structure; removed legacy header/sort UI assertions.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Refactors projects metrics UI for responsive/mobile use, replaces sortable column headers with a Sort-By dropdown and ORDERING_MAP, updates MetricsCard layout and score badge styling, and updates multiple E2E and unit tests to match the new DOM and selectors.

Changes

Cohort / File(s) Summary
E2E & Unit Tests
frontend/__tests__/e2e/pages/ProjectsHealthDashboardMetrics.spec.ts, frontend/__tests__/unit/components/MetricsCard.test.tsx, frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
Updated assertions to use link-filtered selectors and getAllByText(...)[0]; removed sortable-header/header visibility tests; adjusted score selector logic and changed expected classes from text-color to bg-color; added explicit unmounts in per-case loops.
Metrics Page & Ordering
frontend/src/app/projects/dashboard/metrics/page.tsx
Replaced FIELD_MAPPING with ORDERING_MAP and OrderingKey; simplified parseOrderParam; removed SortableColumnHeader; added SORT_FIELDS and a responsive "Sort by" dropdown (ProjectsDashboardDropDown) to drive ordering via URL params; reorganized header layout for mobile.
MetricsCard Component
frontend/src/components/MetricsCard.tsx
Reworked to a stacked/responsive layout, shows "Score: X" badge with bg-color thresholds (green/orange/red), reorganized metrics into a 4-item grid including Health Checked date, and adds fallback for missing project name.
Dropdown Styling
frontend/src/components/ProjectsDashboardDropDown.tsx
Minor styling tweak: added dark-mode text color class for selected labels display.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • arkid15r
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: fixing responsiveness on small screens for the Project Metrics dashboard.
Description check ✅ Passed The description explains the proposed changes, references the resolved issue, provides before/after screenshots, and includes relevant checklist confirmations.
Linked Issues check ✅ Passed The PR addresses all primary objectives from issue #3368: renders correctly on mobile, replaces table layout with responsive cards, prevents text overflow, and adds mobile-friendly sorting control.
Out of Scope Changes check ✅ Passed All changes are scoped to improving Project Metrics dashboard responsiveness; no unrelated modifications detected in the codebase.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@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

Caution

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

⚠️ Outside diff range comments (1)
frontend/__tests__/unit/components/MetricsCard.test.tsx (1)

68-73: Inconsistent query pattern may cause test failures.

This test still uses getByText(score.toString()) while other assertions were updated to getAllByText(...)[0]. If MetricsCard now renders the score in both mobile and desktop layouts, getByText will throw when multiple matches exist. The subsequent .closest('div') call would also select an unpredictable element if multiple scores are present.

Proposed fix
     for (const [score, expectedClass] of cases) {
       const metric = makeMetric({ score })
       render(<MetricsCard metric={metric} />)
-      const scoreEl = screen.getByText(score.toString()).closest('div')
+      const scoreEl = screen.getAllByText(score.toString())[0].closest('div')
       expect(scoreEl).toHaveClass(expectedClass)
     }
🤖 Fix all issues with AI agents
In `@frontend/__tests__/e2e/pages/ProjectsHealthDashboardMetrics.spec.ts`:
- Around line 56-69: The test uses toLocaleString('default', ...) which causes
locale-dependent flakiness; update the expectation to use a fixed explicit
locale matching the MetricsCard (e.g., 'en-US' or the locale used by
MetricsCard) when formatting new Date(firstMetric.createdAt).toLocaleString so
the string comparison is deterministic across CI environments; locate the
assertion building the selector (the page.getByRole('link')...filter(...) that
references new Date(firstMetric.createdAt).toLocaleString) and replace 'default'
with the explicit locale.
🧹 Nitpick comments (5)
frontend/src/app/projects/dashboard/metrics/page.tsx (2)

76-81: Consider aligning mobile sort behavior with desktop for consistency.

The getNextOrderKey helper toggles between descending and ascending only. Desktop sorting (via SortableColumnHeader) has a three-state cycle: unsorted → descending → ascending → unsorted (clearing on third click). Mobile users must explicitly use "Clear sorting" to reset.

This UX difference may be intentional, but verify it matches the expected behavior. If three-state cycling is desired for mobile, the logic would need adjustment.


296-331: Mobile sort dropdown implementation looks good.

The mobile-only sort UI (sm:hidden) with clear sorting action provides a usable experience on small screens. The icon dynamically reflects sort direction, and selectedLabels displays the current sort field.

Minor observation: When urlKey is falsy but a default sort (-score) is applied internally, the dropdown shows no selected label, which may be slightly confusing. Consider showing "Score" as the default selected label for clarity.

frontend/src/components/MetricsCard.tsx (1)

53-109: Well-structured mobile layout that addresses the PR objective.

The mobile card design provides good information hierarchy with the score badge prominently displayed alongside the project name, followed by key metrics in a grid. The stacked layout works well for narrow screens.

Consider extracting the score color logic (lines 69-73 and 44-47) into a shared helper to reduce duplication, though this is a minor maintainability improvement.

♻️ Optional: Extract score color logic
// At the top of the file or in a shared utils file
const getScoreColorClasses = (score: number) => ({
  'bg-green-500': score >= 75,
  'bg-orange-500': score >= 50 && score < 75,
  'bg-red-500': score < 50,
})

Then use in both layouts:

-              {
-                'bg-green-500': metric.score >= 75,
-                'bg-orange-500': metric.score >= 50 && metric.score < 75,
-                'bg-red-500': metric.score < 50,
-              }
+              getScoreColorClasses(metric.score)
frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (1)

244-262: Test selector update accommodates the new responsive layout.

Using getAllByText(...)[0] is a reasonable approach given that the responsive changes render duplicate text in both desktop and mobile layouts. The tests continue to verify that the expected data is present in the document.

For future maintainability, consider adding data-testid attributes to specific elements if more precise targeting becomes necessary, but this is acceptable for now.

frontend/__tests__/e2e/pages/ProjectsHealthDashboardMetrics.spec.ts (1)

32-77: Assertions are broad—consider verifying co-location of values.

Each assertion independently checks that some link contains the expected text. If multiple metric cards exist and happen to share a value (e.g., two projects with the same fork count), these assertions pass without confirming the values belong to the same card. For stronger guarantees, consider asserting that all expected values appear within the same link element:

await expect(metricsLink).toContainText(firstMetric.starsCount.toString())
await expect(metricsLink).toContainText(firstMetric.forksCount.toString())
// etc.

This would verify the first metric card contains all expected values together.

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 18, 2026
@anurag2787 anurag2787 marked this pull request as ready for review January 18, 2026 22:16
@anurag2787
Copy link
Contributor Author

Hi @kasya I’ve added a new card-based layout for the mobile view since the table layout doesn’t really work well on smaller screens and one more thing like while implementing sorting for mobile view i got a bit unsure about the UI. Right now, when a user taps on a field like Stars, it sorts in descending order first, and tapping it again switches to ascending.
Does this interaction make sense for mobile, or do you think there’s a better way to handle sorting direction on smaller screens?

@anurag2787
Copy link
Contributor Author

anurag2787 commented Jan 18, 2026

@kasya @arkid15r
And also while working on this i noticed that Project Dashboard is also not responsive
image
Same with http://localhost:3000/projects/dashboard/metrics/social-osint-agent Page
image

Hi, I’ve created a separate issue for this #3445 about the Project Health Dashboard if you feel this should be handled here instead i will work on it in this pr itself

@ahmedxgouda ahmedxgouda self-assigned this Jan 19, 2026
Copy link
Collaborator

@ahmedxgouda ahmedxgouda left a comment

Choose a reason for hiding this comment

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

Thanks for working on this. Also I think it is better to make the same design for all screens instead of having a table for large screens and a list of cards for mobile screens, we should use your new design for all screens. With this, not also the user will see the same UI/UX in different devices, but the code will be cleaner and more simple. I will leave the decision of changing the design of large screens to @kasya. Please see the below comments.

@anurag2787 anurag2787 force-pushed the fix/responsive-project-matrics branch from a96cbea to 075698e Compare January 20, 2026 19:55
Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@frontend/src/app/projects/dashboard/metrics/page.tsx`:
- Around line 299-330: The icon selection currently uses urlKey?.startsWith('-')
which no longer matches new keys like scoreDesc/starsAsc; update the icon prop
on ProjectsDashboardDropDown to derive direction from the urlKey suffix (e.g.,
use urlKey?.endsWith('Desc') to pick FaArrowDownWideShort, otherwise
FaArrowUpWideShort) and ensure it falls back correctly when urlKey is
null/empty; reference urlKey, MOBILE_SORT_FIELDS and handleSort when making the
change so mobile sort display and behaviour remain consistent.
🧹 Nitpick comments (1)
frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (1)

60-286: Consider adding test coverage for the mobile sort dropdown.

The test file has been updated for the new ordering keys, but there's no explicit test verifying the new mobile "Sort By" dropdown functionality. Consider adding a test that verifies:

  • The mobile sort dropdown renders with correct options
  • Selecting a sort option triggers handleSort with the appropriate key
  • The "Reset Sorting" option calls handleSort(null)

This would help ensure the new mobile sorting feature works as expected.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@frontend/src/app/projects/dashboard/metrics/page.tsx`:
- Around line 58-66: The parseOrderParam function currently uses the `in`
operator against MOBILE_ORDERING_MAP which allows prototype keys (e.g.,
"toString") to pass validation; change the check to an own-property check (e.g.,
Object.prototype.hasOwnProperty.call(MOBILE_ORDERING_MAP, orderParam)) so only
true keys in MOBILE_ORDERING_MAP are accepted, then safely read { field,
direction } from MOBILE_ORDERING_MAP[orderParam as MobileOrderingKey] and fall
back to the default when the key is not an own property or values are missing.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@frontend/src/app/projects/dashboard/metrics/page.tsx`:
- Around line 300-329: The dropdown was changed to selectionMode="single", which
breaks previous multi-criteria sorting support; either revert selectionMode back
to "multiple" on ProjectsDashboardDropDown and ensure onAction/handleSort and
MOBILE_ORDERING_MAP accept an array of keys (update handleSort to accept
multiple keys and apply MOBILE_ORDERING_MAP accordingly), or explicitly keep
selectionMode="single" but then update handleSort and MOBILE_ORDERING_MAP to
only accept a single key and remove any multi-sort assumptions (choose one
consistent approach between ProjectsDashboardDropDown, handleSort,
MOBILE_ORDERING_MAP, and MOBILE_SORT_FIELDS).

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 21, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 21, 2026
@kasya
Copy link
Collaborator

kasya commented Jan 23, 2026

Thanks for working on this. Also I think it is better to make the same design for all screens instead of having a table for large screens and a list of cards for mobile screens, we should use your new design for all screens. With this, not also the user will see the same UI/UX in different devices, but the code will be cleaner and more simple.

I agree! I like this new design 👍🏼 Let's make it the same for all screen sizes.

@anurag2787 anurag2787 marked this pull request as draft January 23, 2026 17:16
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 24, 2026
@anurag2787 anurag2787 marked this pull request as ready for review January 24, 2026 08:53
@anurag2787 anurag2787 requested a review from kasya January 24, 2026 08:54
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 24, 2026
@anurag2787
Copy link
Contributor Author

image

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@frontend/src/components/MetricsCard.tsx`:
- Around line 66-72: The date rendering in the MetricsCard component uses
toLocaleString('default', ...) which can differ between server and client and
cause hydration mismatches; update the rendering to use a fixed locale (e.g.,
replace 'default' with 'en-US') or move the formatting to client-only rendering
(e.g., compute formattedDate in a useEffect/useState) so the output is
deterministic; target the JSX that formats metric.createdAt in MetricsCard and
ensure the change uses a stable locale or client-side formatting.
🧹 Nitpick comments (2)
frontend/src/components/MetricsCard.tsx (2)

36-36: Minor: Redundant margin classes.

The my-4 on the <hr> may create extra spacing since the parent uses space-y-4 which already adds margin between children. Consider removing my-4 if the spacing looks excessive.


14-21: Consider using a falsy check instead of strict empty-string comparison.

The metric.projectName field is optional (projectName?: string in HealthMetricsProps), meaning it can be undefined. The strict equality check metric.projectName === '' only handles empty strings, leaving undefined values unhandled—the "No name" fallback won't display for undefined values.

♻️ Suggested fix
             <p
               className={clsx(
                 'text-md truncate font-semibold lg:text-xl',
-                metric.projectName === '' ? 'text-gray-500' : 'text-gray-800 dark:text-gray-200'
+                !metric.projectName ? 'text-gray-500' : 'text-gray-800 dark:text-gray-200'
               )}
             >
-              {metric.projectName === '' ? 'No name' : metric.projectName}
+              {metric.projectName || 'No name'}
             </p>

@codecov
Copy link

codecov bot commented Jan 25, 2026

Codecov Report

❌ Patch coverage is 52.63158% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.48%. Comparing base (4643156) to head (222879c).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
...ontend/src/app/projects/dashboard/metrics/page.tsx 43.75% 6 Missing and 3 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3412      +/-   ##
==========================================
- Coverage   85.61%   85.48%   -0.13%     
==========================================
  Files         461      461              
  Lines       14254    14240      -14     
  Branches     1905     1896       -9     
==========================================
- Hits        12203    12173      -30     
- Misses       1672     1688      +16     
  Partials      379      379              
Flag Coverage Δ
backend 84.48% <ø> (+<0.01%) ⬆️
frontend 88.26% <52.63%> (-0.47%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
frontend/src/components/MetricsCard.tsx 100.00% <100.00%> (ø)
...ntend/src/components/ProjectsDashboardDropDown.tsx 100.00% <ø> (ø)
...ontend/src/app/projects/dashboard/metrics/page.tsx 74.33% <43.75%> (-15.67%) ⬇️

... and 3 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4643156...222879c. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud
Copy link

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@anurag2787 this looks good, thank you! 👍🏼

I pushed a little change to update card styling for larger screens and how we show Time health was checked - with that change the card is smaller on larger screens and doesn't have too much empty space.

@kasya kasya dismissed ahmedxgouda’s stale review January 25, 2026 00:50

Already resolved

@kasya kasya added this pull request to the merge queue Jan 25, 2026
Merged via the queue into OWASP:main with commit 0299256 Jan 25, 2026
35 of 37 checks passed
This was referenced Jan 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project Metrics dashboard is not responsive on smaller screens

3 participants