diff --git a/app/projects/[id]/page.module.css b/app/projects/[id]/page.module.css index 5698f41d..918d995c 100644 --- a/app/projects/[id]/page.module.css +++ b/app/projects/[id]/page.module.css @@ -38,6 +38,7 @@ .project__container__details__additionalDetails, .project__container__info__contributors, .project__container__info__teams, +.project__container__details__stats, .project__container__info__contacts { padding: 16px; background-color: #ffffff; @@ -76,6 +77,7 @@ .project__container__details__additionalDetails, .project__container__info__contributors, .project__container__info__teams, + .project__container__details__stats, .project__container__info__contacts { padding: 16px; box-shadow: 0px 4px 4px 0px rgba(15, 23, 42, 0.04), 0px 0px 1px 0px rgba(15, 23, 42, 0.12); diff --git a/app/projects/[id]/page.tsx b/app/projects/[id]/page.tsx index 0deb0912..72fc03ed 100644 --- a/app/projects/[id]/page.tsx +++ b/app/projects/[id]/page.tsx @@ -8,7 +8,7 @@ import Header from '@/components/page/project-details/header'; import Hyperlinks from '@/components/page/project-details/hyper-links'; import KPIs from '@/components/page/project-details/kpis'; import TeamsInvolved from '@/components/page/project-details/teams-involved'; -import { getProject } from '@/services/projects.service'; +import { getProject, getProjectOsoDetails } from '@/services/projects.service'; import { getAllTeams } from '@/services/teams.service'; import { hasProjectDeleteAccess, hasProjectEditAccess } from '@/utils/common.utils'; import { getCookiesFromHeaders } from '@/utils/next-helpers'; @@ -18,10 +18,12 @@ import { IFocusArea } from '@/types/shared.types'; import SelectedFocusAreas from '@/components/core/selected-focus-area'; import { PAGE_ROUTES, SOCIAL_IMAGE_URL } from '@/utils/constants'; import { Metadata, ResolvingMetadata } from 'next'; +import ProjectStats from '@/components/page/project-details/stats'; export default async function ProjectDetails({ params }: any) { const projectId = params?.id; - const { isError, userInfo, hasEditAccess, hasDeleteAccess, project, focusAreas, authToken } = await getPageData(projectId); + const { isError, userInfo, hasEditAccess, hasDeleteAccess, project, focusAreas, authToken, osoInfo } = await getPageData(projectId); + const showProjectStats = osoInfo?.forkCount > 0 || osoInfo?.starCount > 0 || osoInfo?.repositoryCount > 0 || osoInfo?.contributorCount > 0; if (isError) { return ; @@ -36,7 +38,7 @@ export default async function ProjectDetails({ params }: any) {
- +
{project?.projectLinks?.length > 0 && ( @@ -58,6 +60,12 @@ export default async function ProjectDetails({ params }: any) {
)} + {showProjectStats && ( +
+ +
+ )} +
@@ -86,6 +94,7 @@ const getPageData = async (projectId: string) => { let isError = false; const { authToken, isLoggedIn, userInfo } = getCookiesFromHeaders(); let project = null; + let osoInfo = null; let hasEditAccess = false; let hasDeleteAccess = false; let loggedInMemberTeams = []; @@ -93,7 +102,6 @@ const getPageData = async (projectId: string) => { try { const [projectResponse, focusAreaResponse] = await Promise.all([getProject(projectId, {}), getFocusAreas('Project', {})]); - if (projectResponse?.error || focusAreaResponse?.error) { return { isError: true, @@ -124,6 +132,12 @@ const getPageData = async (projectId: string) => { project = projectResponse?.data?.formattedData; focusAreas = focusAreaResponse?.data?.filter((data: IFocusArea) => !data.parentUid); + if (project.osoProjectName) { + const osoResponse = await getProjectOsoDetails(project.osoProjectName); + if (!osoResponse?.error) { + osoInfo = osoResponse?.data ?? {}; + } + } hasEditAccess = hasProjectEditAccess(userInfo, project, isLoggedIn, loggedInMemberTeams); hasDeleteAccess = hasProjectDeleteAccess(userInfo, project, isLoggedIn); @@ -137,6 +151,7 @@ const getPageData = async (projectId: string) => { project, focusAreas, authToken, + osoInfo, }; } catch (error) { return { @@ -148,11 +163,11 @@ const getPageData = async (projectId: string) => { project, focusAreas, authToken, + osoInfo, }; } }; - type IGenerateMetadata = { params: { id: string }; searchParams: { [key: string]: string | string[] | undefined }; diff --git a/components/page/project-details/stats-card.tsx b/components/page/project-details/stats-card.tsx new file mode 100644 index 00000000..964a5a5c --- /dev/null +++ b/components/page/project-details/stats-card.tsx @@ -0,0 +1,89 @@ +'use client'; + +import Image from 'next/image'; + +interface IStatsCard { + statsName: string; + count: number; + icon: string; + newContributors?: number; +} + +const StatsCard = (props: IStatsCard) => { + const statsName = props?.statsName; + const count = props?.count; + const icon = props?.icon; + const newContributors = props?.newContributors ?? 0; + + return ( + <> +
+

+ {count} {statsName === 'Contributors' && newContributors > 0 && {`(${newContributors} New)`}} +

+
+ icon + {statsName} +
+ {(statsName === 'Contributors' && newContributors > 0) && Last 6 months} +
+ + + ); +}; + +export default StatsCard; diff --git a/components/page/project-details/stats.tsx b/components/page/project-details/stats.tsx new file mode 100644 index 00000000..1bd13b2c --- /dev/null +++ b/components/page/project-details/stats.tsx @@ -0,0 +1,69 @@ +'use client'; + +import StatsCard from './stats-card'; + +interface IProjectStats { + stats: any; +} + +const ProjectStats = (props: IProjectStats) => { + const stats = props?.stats; + + return ( + <> +
+
Project Stats
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + + ); +}; + +export default ProjectStats; diff --git a/public/icons/contributors.svg b/public/icons/contributors.svg new file mode 100644 index 00000000..33efd6fb --- /dev/null +++ b/public/icons/contributors.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/fork.svg b/public/icons/fork.svg new file mode 100644 index 00000000..a961c0d1 --- /dev/null +++ b/public/icons/fork.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/repos.svg b/public/icons/repos.svg new file mode 100644 index 00000000..6b6699de --- /dev/null +++ b/public/icons/repos.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/services/projects.service.ts b/services/projects.service.ts index 303efb86..17eea237 100644 --- a/services/projects.service.ts +++ b/services/projects.service.ts @@ -73,6 +73,7 @@ const getFormattedProject = (project: any) => { formattedProject['createdBy'] = project.createdBy ?? null; formattedProject['score'] = project.score ?? null; formattedProject['projectFocusAreas']= project.projectFocusAreas ?? []; + formattedProject['osoProjectName'] = project.osoProjectName ?? null; const tempContributors: any = []; project?.contributions?.map((mem: any) => { @@ -175,4 +176,15 @@ const formatToSave = (payload: any) => { objectToSave['contributions'] = payload?.contributions; objectToSave['focusAreas'] = []; return objectToSave; +} + +export const getProjectOsoDetails = async (name: string) => { + const requestOptions: RequestInit = { method: "GET", headers: getHeader(""), cache: "no-store" }; + const response = await fetch(`${process.env.DIRECTORY_API_URL}/v1/oso-metrics/${name}`, requestOptions); + if (!response?.ok) { + return { error: { statusText: response?.statusText } } + } + + const result = await response?.json(); + return { data: result } } \ No newline at end of file