-
-
Notifications
You must be signed in to change notification settings - Fork 295
Implement the detailed metrics dashboard page #1748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
a5ad675
b5ebc3b
271adee
1988615
5a64432
c06e6bf
22ff74d
4f121cd
881ecaa
459e998
279df43
e8400c4
7831e9f
a8e533c
a1e84a4
7a166c4
17fc95b
ac69330
92688ee
55b33f4
7a9c7b2
373e16f
e8e7375
ed476bc
74bcd72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| 'use client' | ||
|
|
||
| import { useQuery } from '@apollo/client' | ||
| import { | ||
| faPeopleGroup, | ||
| faCodeFork, | ||
| faDollar, | ||
| faCodePullRequest, | ||
| faChartArea, | ||
| faExclamationCircle, | ||
| faQuestionCircle, | ||
| faFolderOpen, | ||
| faHandshake, | ||
| faTag, | ||
| faRocket, | ||
| faStar, | ||
| faTags, | ||
| } from '@fortawesome/free-solid-svg-icons' | ||
| import millify from 'millify' | ||
| import { useParams } from 'next/navigation' | ||
| import { FC, useState, useEffect } from 'react' | ||
| import { handleAppError } from 'app/global-error' | ||
| import { GET_PROJECT_HEALTH_METRICS_DETAILS } from 'server/queries/projectsHealthDashboardQueries' | ||
| import { HealthMetricsProps } from 'types/healthMetrics' | ||
| import BarChart from 'components/BarChart' | ||
| import DashboardCard from 'components/DashboardCard' | ||
| import GeneralCompliantComponent from 'components/GeneralCompliantComponent' | ||
| import LoadingSpinner from 'components/LoadingSpinner' | ||
| import MetricsScoreCircle from 'components/MetricsScoreCircle' | ||
| const ProjectHealthMetricsDetails: FC = () => { | ||
| const { projectKey } = useParams() | ||
| const [metrics, setMetrics] = useState<HealthMetricsProps>() | ||
| const { | ||
| loading, | ||
| error: graphqlError, | ||
| data, | ||
| } = useQuery(GET_PROJECT_HEALTH_METRICS_DETAILS, { | ||
| variables: { projectKey }, | ||
| }) | ||
|
|
||
| useEffect(() => { | ||
| if (graphqlError) { | ||
| handleAppError(graphqlError) | ||
| } | ||
| if (data?.project?.healthMetricsLatest) { | ||
| setMetrics(data.project.healthMetricsLatest) | ||
| } | ||
| }, [graphqlError, data]) | ||
| if (loading) { | ||
| return <LoadingSpinner /> | ||
| } | ||
|
|
||
| return ( | ||
| <div className="flex flex-col gap-4"> | ||
| {metrics && ( | ||
| <> | ||
| <div className="flex items-center justify-between"> | ||
| <h1 className="text-2xl font-bold">{metrics.projectName}</h1> | ||
| <MetricsScoreCircle score={metrics.score} /> | ||
| </div> | ||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-2"> | ||
| <GeneralCompliantComponent | ||
ahmedxgouda marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| title="Funding Requirements Compliant" | ||
| icon={faDollar} | ||
| compliant={metrics.isFundingRequirementsCompliant} | ||
| /> | ||
| <GeneralCompliantComponent | ||
| title="Leader Requirements Compliant" | ||
| icon={faHandshake} | ||
| compliant={metrics.isLeaderRequirementsCompliant} | ||
| /> | ||
| </div> | ||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-3"> | ||
| <DashboardCard title="Stars" icon={faStar} stats={millify(metrics.starsCount)} /> | ||
| <DashboardCard title="Forks" icon={faCodeFork} stats={millify(metrics.forksCount)} /> | ||
| <DashboardCard | ||
| title="Contributors" | ||
| icon={faPeopleGroup} | ||
| stats={millify(metrics.contributorsCount)} | ||
| /> | ||
| </div> | ||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-3 lg:grid-cols-4"> | ||
| <DashboardCard | ||
| title="Open Issues" | ||
| icon={faExclamationCircle} | ||
| stats={millify(metrics.openIssuesCount)} | ||
| /> | ||
| <DashboardCard | ||
| title="Total Issues" | ||
| icon={faFolderOpen} | ||
| stats={millify(metrics.totalIssuesCount)} | ||
| /> | ||
| <DashboardCard | ||
| title="Unassigned Issues" | ||
| icon={faTag} | ||
| stats={millify(metrics.unassignedIssuesCount)} | ||
| /> | ||
| <DashboardCard | ||
| title="Unanswered Issues" | ||
| icon={faQuestionCircle} | ||
| stats={millify(metrics.unansweredIssuesCount)} | ||
| /> | ||
| </div> | ||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-3"> | ||
| <DashboardCard | ||
ahmedxgouda marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| title="Open Pull Requests" | ||
| icon={faCodePullRequest} | ||
| stats={millify(metrics.openPullRequestsCount)} | ||
| /> | ||
| <DashboardCard | ||
| title="Recent Releases" | ||
| icon={faRocket} | ||
| stats={millify(metrics.recentReleasesCount)} | ||
| /> | ||
| <DashboardCard | ||
| title="Total Releases" | ||
| icon={faTags} | ||
| stats={millify(metrics.totalReleasesCount)} | ||
| /> | ||
| </div> | ||
| <BarChart | ||
| title="Days Metrics" | ||
| icon={faChartArea} | ||
| labels={[ | ||
| 'Project Age', | ||
| 'Days Since Last Commit', | ||
| 'Days Since Last Release', | ||
| 'Days Since Last Pull Request', | ||
| 'Days Since OWASP Page Last Update', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This text is a part of the apexcharts Chart component. So, I don't have full control on it. Maybe it can be done, I will take a look.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ahmedxgouda Gotcha! I'll check that too! Thank you!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kasya Didn't find a way, but a tooltip will show when the user hover on the bar itself. |
||
| ]} | ||
| days={[ | ||
| metrics.ageDays, | ||
| metrics.lastCommitDays, | ||
| metrics.lastReleaseDays, | ||
| metrics.lastPullRequestDays, | ||
| metrics.owaspPageLastUpdateDays, | ||
| ]} | ||
| requirements={[ | ||
| metrics.ageDaysRequirement, | ||
| metrics.lastCommitDaysRequirement, | ||
| metrics.lastReleaseDaysRequirement, | ||
| metrics.lastPullRequestDaysRequirement, | ||
| metrics.owaspPageLastUpdateDaysRequirement, | ||
| ]} | ||
| reverseColors={[true, false, false, false, false]} | ||
| /> | ||
| </> | ||
| )} | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| export default ProjectHealthMetricsDetails | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify the expected return type for health_metrics_latest field.
The test uses
field.type.of_typewhich is typically used for list types, but the field namehealth_metrics_latestsuggests it should return a singleProjectHealthMetricsNodeobject, not a list.If
health_metrics_latestreturns a single object, the assertion should be:If it returns a list, then the current assertion is correct.
🏁 Script executed:
Length of output: 161
Incorrect assertion of health_metrics_latest return type
The health_metrics_latest field is defined to return a single ProjectHealthMetricsNode (or null), not a list. The test should assert the field’s type directly, not its element type.
File: backend/tests/apps/owasp/graphql/nodes/project_test.py (lines 47–50)
📝 Committable suggestion
🤖 Prompt for AI Agents