diff --git a/src/app/components/cards/OrganisationCard.tsx b/src/app/components/cards/OrganisationCard.tsx
index b727eac..cc2b53a 100644
--- a/src/app/components/cards/OrganisationCard.tsx
+++ b/src/app/components/cards/OrganisationCard.tsx
@@ -6,7 +6,6 @@ type OrganisationCardProps = {
login: string
avatarUrl: string
handleClick: (type: string) => void
- more: boolean
toggleFilter: string | null
}
@@ -14,20 +13,13 @@ export const OrganisationCard = ({
login,
avatarUrl,
handleClick,
- more,
toggleFilter
}: OrganisationCardProps) => {
return (
handleClick(login)}
@@ -52,61 +44,22 @@ export const ProjectsBlock = ({
toggleFilter,
handleFilterToggle
}: {
- projects: Array
+ projects: Array
toggleFilter: string | null
handleFilterToggle: (key: string) => void
}) => {
- const [showMore, setShowMore] = React.useState(false)
-
return (
{projects.length > 0 &&
- projects
- .slice(0, 3)
- .map((x, idx) => (
-
- ))}
- {projects.length > 3 ? (
-
-
- {showMore ? (
-
setShowMore(!showMore)}
- >
-
-
- projects
-
-
- {projects.slice(3).map((x, idx) => (
-
- ))}
-
-
-
- ) : null}
-
- ) : null}
+ projects.map((x, idx) => (
+
+ ))}
)
}
diff --git a/src/app/components/contribution-graph/index.tsx b/src/app/components/contribution-graph/index.tsx
new file mode 100644
index 0000000..2fd8db0
--- /dev/null
+++ b/src/app/components/contribution-graph/index.tsx
@@ -0,0 +1,65 @@
+import { days, months } from "@/helpers/utils"
+import { Contribution } from "@/types"
+import React from "react"
+import ToolTip from "../tool-tip"
+
+const ContributionGraph = ({
+ memoizedGraphValues,
+ loading,
+ onClickToolTip
+}: {
+ memoizedGraphValues: Contribution[]
+ onClickToolTip: (content: Contribution) => void
+ loading: boolean
+}) => {
+ return (
+
+
+
+ {days.map((d) => (
+
+ {d}
+
+ ))}
+
+
+
+ {months.map((m) => (
+
+ {m}
+
+ ))}
+
+
+
+ {memoizedGraphValues.map((day, idx) => (
+
+ ))}
+
+
+
+
+
+
+ )
+}
+
+export default ContributionGraph
diff --git a/src/app/components/search/action.ts b/src/app/components/search/action.ts
index da9095f..4098467 100644
--- a/src/app/components/search/action.ts
+++ b/src/app/components/search/action.ts
@@ -2,8 +2,20 @@
import { auth } from "@/auth"
import { getGithubData } from "@/helpers/get-github-data"
+import { getIssueCommentsData } from "@/helpers/get-issues-data"
+import { getGithubPrsData } from "@/helpers/get-prs-data"
-export const fetchIssues = async ({ username }: { username: string }) => {
+export const fetchIssues = async ({
+ username,
+ startDate,
+ endDate,
+ endCursor
+}: {
+ username: string
+ startDate?: string
+ endDate?: string
+ endCursor: string
+}) => {
const session = await auth()
const token = session?.accessToken
@@ -20,7 +32,32 @@ export const fetchIssues = async ({ username }: { username: string }) => {
})
])
+ const ranged_response = await Promise.all([
+ getGithubPrsData({
+ username,
+ token,
+ query: "FETCH_RANGED_PRS",
+ startDate,
+ endDate
+ }),
+ getIssueCommentsData({
+ username,
+ token,
+ query: "FETCH_RANGED_COMMENTS",
+ startDate,
+ endCursor
+ })
+ ])
+
const issues = res[0]
const prs = res[1]
- return { issues, prs }
+ const ranged_prs = ranged_response[0]
+ const ranged_issues = ranged_response[1]
+
+ return {
+ issues,
+ prs,
+ ranged_prs,
+ ranged_issues
+ }
}
diff --git a/src/app/components/search/contribution-years.ts b/src/app/components/search/contribution-years.ts
new file mode 100644
index 0000000..28221b0
--- /dev/null
+++ b/src/app/components/search/contribution-years.ts
@@ -0,0 +1,22 @@
+"use server"
+
+import { auth } from "@/auth"
+import { getContributionYears } from "@/helpers/get-contribution-years"
+
+export const fetchYears = async ({ params }: { params: any }) => {
+ const session = await auth()
+ const token = session?.accessToken
+
+ const res = await Promise.all([
+ getContributionYears({
+ token,
+ ...params
+ })
+ ])
+
+ const years = res[0]
+
+ return {
+ years
+ }
+}
diff --git a/src/app/components/search/index.tsx b/src/app/components/search/index.tsx
index 6e82da8..59a8415 100644
--- a/src/app/components/search/index.tsx
+++ b/src/app/components/search/index.tsx
@@ -30,12 +30,12 @@ export default function Search() {
setError(null)
const { issues, prs } = await fetchIssues({
- username: username as string
+ username: username as string,
+ endCursor: ""
})
setLoading(false)
if (issues.error || prs.error) {
- console.error(issues.error, "error")
setError(issues.error[0].message || prs.error[0].message)
return
}
diff --git a/src/app/components/years-switch/index.tsx b/src/app/components/years-switch/index.tsx
index 04df2ee..e315046 100644
--- a/src/app/components/years-switch/index.tsx
+++ b/src/app/components/years-switch/index.tsx
@@ -12,7 +12,7 @@ const YearSection = ({ years, currentYear, handleClick }: YearSectionProps) => {
const [showYears, setShowYears] = React.useState(false)
return (
-
+
{years.map((year) => (
{
{showYears ? (
-
+
{years.slice(1).map((year) => (
{
const searchParams = useSearchParams()
@@ -23,11 +24,11 @@ const Page = () => {
toggleFilter,
yearlyFilter,
handleYearlyFilter,
- years,
memoizedGraphValues,
onClickToolTip,
goBack
} = useGithubIssues()
+ const { years } = useGetYears()
return (
@@ -60,32 +61,11 @@ const Page = () => {
-
-
- {memoizedGraphValues.map((day, idx) => (
-
- ))}
-
-
-
+
{
- const rawComments = data?.issueComments?.nodes.filter(
- (comment: any) => comment.issue.author.login === username
+ data = data ?? []
+
+ const rawComments = data?.filter(
+ (comment) => comment.issue.author.login === username
)
- const comments: Comment[] = rawComments.map((comment: any) => {
+ const comments: Comment[] = rawComments?.map((comment) => {
return {
body: comment.body,
repository: comment.repository.url,
@@ -34,14 +37,16 @@ export const getOthersComments = ({
data,
username
}: {
- data: any
+ data: IssueCommentNodes[]
username: string
}): Comment[] => {
- const rawComments = data?.issueComments?.nodes.filter(
- (comment: any) => comment.issue.author.login !== username
+ data = data ?? []
+
+ const rawComments = data?.filter(
+ (comment) => comment.issue.author.login !== username
)
- const comments: Comment[] = rawComments.map((comment: any) => {
+ const comments: Comment[] = rawComments?.map((comment) => {
return {
body: comment.body,
repository: comment.repository.url,
@@ -60,12 +65,18 @@ export const getOthersComments = ({
return comments
}
-export const getLongComments = ({ data }: { data: any }): Comment[] => {
- const rawComments = data?.issueComments?.nodes.filter(
- (comment: any) => comment.body.length > 500
+export const getLongComments = ({
+ data
+}: {
+ data: IssueCommentNodes[]
+}): Comment[] => {
+ data = data ?? []
+
+ const rawComments = data?.filter(
+ (comment) => comment.body.length > MAX_COMMENT_LENGTH
)
- const comments: Comment[] = rawComments.map((comment: any) => {
+ const comments: Comment[] = rawComments?.map((comment) => {
return {
body: comment.body,
repository: comment.repository.url,
diff --git a/src/helpers/get-contribution-years.ts b/src/helpers/get-contribution-years.ts
new file mode 100644
index 0000000..a589a6f
--- /dev/null
+++ b/src/helpers/get-contribution-years.ts
@@ -0,0 +1,52 @@
+import { FETCH_CONTRIBUTION_YEARS } from "@/graphql/queries"
+import { PrDataType } from "@/types/pull_requests"
+
+export async function getContributionYears({
+ token,
+ username
+}: {
+ token?: string
+ username: string
+}) {
+ const tokenFromEnv = process.env.GITHUB_TOKEN
+
+ try {
+ const res = await fetch("https://api.github.com/graphql", {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${token ?? tokenFromEnv}`,
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ query: FETCH_CONTRIBUTION_YEARS,
+ variables: {
+ username
+ }
+ })
+ })
+
+ if (!res.ok) {
+ return { error: res.statusText, message: "Failed to fetch API" }
+ }
+
+ const data = await res.json()
+ const jsonData: PrDataType = data.data.user
+
+ const contributionYears =
+ jsonData?.contributionsCollection.contributionYears
+
+ if (data.errors) {
+ return { error: data.errors, message: "Failed to fetch API" }
+ }
+
+ return {
+ data: contributionYears,
+ error: null
+ }
+ } catch (error) {
+ return {
+ error: error,
+ message: "Failed to fetch API"
+ }
+ }
+}
diff --git a/src/helpers/get-github-data.ts b/src/helpers/get-github-data.ts
index 39cc1c6..253cd0d 100644
--- a/src/helpers/get-github-data.ts
+++ b/src/helpers/get-github-data.ts
@@ -38,7 +38,6 @@ export async function getGithubData({
})
})
if (!res.ok) {
- console.log("res", res)
return { error: res.statusText, message: "Failed to fetch API" }
}
@@ -52,7 +51,6 @@ export async function getGithubData({
error: null
}
} catch (error) {
- console.log(error)
return {
error: error,
message: "Failed to fetch API"
diff --git a/src/helpers/get-issues-data.ts b/src/helpers/get-issues-data.ts
new file mode 100644
index 0000000..579902c
--- /dev/null
+++ b/src/helpers/get-issues-data.ts
@@ -0,0 +1,143 @@
+import { FETCH_RANGED_COMMENTS } from "@/graphql/queries"
+import {
+ IssueCommentDataType,
+ IssueCommentNodes,
+ IssueComments
+} from "@/types/comments"
+
+export async function getIssueCommentsData({
+ username,
+ token,
+ query,
+ startDate,
+ endCursor
+}: {
+ username: string
+ token?: string
+ query: "FETCH_RANGED_COMMENTS"
+ startDate?: string
+ endCursor: string | null
+}) {
+ const tokenFromEnv = process.env.GITHUB_TOKEN
+ let hasNextPage = true
+ const currentDate = new Date()
+ const currentYear = currentDate.getFullYear()
+ const prevYear = currentYear - 1
+ const getYearFromDate = startDate?.slice(0, 4)
+
+ endCursor = Number(getYearFromDate) <= prevYear ? endCursor : null
+
+ if (
+ getYearFromDate !== currentYear.toString() &&
+ getYearFromDate !== prevYear.toString()
+ ) {
+ return {
+ data: [],
+ error: null
+ }
+ }
+
+ let graphqlQuery = ""
+ switch (query) {
+ case "FETCH_RANGED_COMMENTS":
+ graphqlQuery = FETCH_RANGED_COMMENTS
+ break
+ default:
+ throw new Error("Invalid query")
+ }
+
+ let startCursorObj: any = {}
+ let endCursorObj: any = {}
+
+ let allIssueComments: Array = []
+
+ while (hasNextPage) {
+ try {
+ const res = await fetch("https://api.github.com/graphql", {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${token ?? tokenFromEnv}`,
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ query: graphqlQuery,
+ variables: {
+ username,
+ endCursor
+ }
+ })
+ })
+
+ if (!res.ok) {
+ return { error: res.statusText, message: "Failed to fetch API" }
+ }
+
+ const data = await res.json()
+ const jsonData: IssueCommentDataType = data.data.user
+
+ const nodes_data = jsonData?.issueComments?.nodes
+ const start_cursor = jsonData?.issueComments?.pageInfo?.startCursor
+ const isNextPage = jsonData?.issueComments?.pageInfo?.hasNextPage
+ const end_cursor = jsonData?.issueComments?.pageInfo?.endCursor
+
+ // get previous year from start date of query
+ const prevYear = Number(getYearFromDate) - 1
+
+ const findNextYear = nodes_data?.find(
+ // return first year that is a previous year, we are slicing the createdAt date to get current year of eaxh result
+ (x) => x.createdAt.slice(0, 4) === prevYear.toString()
+ )
+
+ if (
+ findNextYear ||
+ (isNextPage === false && findNextYear === undefined)
+ ) {
+ // filter previous year from response data, when previous year is found in current query date range
+ const filter_node_data = nodes_data.filter((x) => {
+ return x.createdAt.slice(0, 4) === startDate?.slice(0, 4)
+ })
+
+ const res = filter_node_data
+
+ allIssueComments.push(...res)
+
+ hasNextPage = false
+ endCursor = end_cursor
+
+ endCursorObj["end_cursor"] = endCursor
+ } else {
+ allIssueComments.push(...nodes_data)
+ hasNextPage = isNextPage
+
+ endCursor = end_cursor
+ startCursorObj["start_cursor"] = start_cursor
+ endCursorObj["end_cursor"] = endCursor
+ }
+
+ if (data.errors) {
+ return { error: data.errors, message: "Failed to fetch API" }
+ }
+ } catch (error) {
+ return {
+ error: error,
+ message: "Failed to fetch API"
+ }
+ }
+ }
+
+ allIssueComments
+ .sort(
+ (a, b) =>
+ new Date(a?.createdAt).getTime() -
+ new Date(b?.createdAt).getTime()
+ )
+ .filter((x) => x !== undefined)
+
+ return {
+ data: allIssueComments,
+ error: null,
+ startCursorObj,
+ endCursorObj,
+ hasNextPage
+ }
+}
diff --git a/src/helpers/get-prs-data.ts b/src/helpers/get-prs-data.ts
new file mode 100644
index 0000000..4fd7d3f
--- /dev/null
+++ b/src/helpers/get-prs-data.ts
@@ -0,0 +1,94 @@
+import { FETCH_RANGED_PRS } from "@/graphql/queries"
+import { PrDataType, PrNodes } from "@/types/pull_requests"
+
+export async function getGithubPrsData({
+ username,
+ token,
+ query,
+ startDate,
+ endDate
+}: {
+ username: string
+ token?: string
+ query: "FETCH_RANGED_PRS"
+ startDate?: string
+ endDate?: string
+}) {
+ const tokenFromEnv = process.env.GITHUB_TOKEN
+
+ let graphqlQuery = ""
+ switch (query) {
+ case "FETCH_RANGED_PRS":
+ graphqlQuery = FETCH_RANGED_PRS
+ break
+ default:
+ throw new Error("Invalid query")
+ }
+
+ let hasNextPage = true
+ let allPrs: Array = []
+ let endCursor = null
+
+ while (hasNextPage) {
+ try {
+ const res = await fetch("https://api.github.com/graphql", {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${token ?? tokenFromEnv}`,
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ query: graphqlQuery,
+ variables: {
+ username,
+ startDate,
+ endDate,
+ endCursor
+ }
+ })
+ })
+
+ if (!res.ok) {
+ return { error: res.statusText, message: "Failed to fetch API" }
+ }
+
+ const data = await res.json()
+ const jsonData: PrDataType = data.data.user
+
+ const isNextPage =
+ jsonData?.contributionsCollection?.pullRequestContributions
+ ?.pageInfo.hasNextPage
+
+ const nodes_data =
+ jsonData?.contributionsCollection?.pullRequestContributions
+ .nodes
+
+ const next_batch =
+ jsonData?.contributionsCollection?.pullRequestContributions
+ ?.pageInfo?.endCursor
+
+ allPrs.push(...nodes_data)
+ hasNextPage = isNextPage
+ endCursor = next_batch
+
+ if (data.errors) {
+ return { error: data.errors, message: "Failed to fetch API" }
+ }
+ } catch (error) {
+ return {
+ error: error,
+ message: "Failed to fetch API"
+ }
+ }
+ }
+
+ allPrs
+ .sort(
+ (a, b) =>
+ new Date(a?.pullRequest.createdAt).getTime() -
+ new Date(b?.pullRequest.createdAt).getTime()
+ )
+ .filter((x) => x?.pullRequest !== undefined)
+
+ return { data: allPrs, error: null }
+}
diff --git a/src/helpers/get-pull-requests.ts b/src/helpers/get-pull-requests.ts
index e7b591b..251dd6f 100644
--- a/src/helpers/get-pull-requests.ts
+++ b/src/helpers/get-pull-requests.ts
@@ -1,104 +1,111 @@
import { CURRENT_DAY, DAYS_TO_INACTIVE, ONE_DAY } from "@/config"
-import { PullRequests, PR } from "../types/pull_requests"
+import { PullRequests, PR, PrNodes } from "../types/pull_requests"
export const getPullRequests = ({
data,
username
}: {
- data: any
+ data: PrNodes[]
username: string
}): PullRequests => {
- const openPRsData = data.pullRequests.edges.filter(
- (pr: any) => pr.node.closed === false
- )
- const openPRs: PR[] = openPRsData.map((pr: any) => ({
- totalComments: pr.node.totalCommentsCount,
+ data = data ?? []
+
+ const openPRsData = data?.filter((pr) => pr?.pullRequest.closed === false)
+
+ const openPRs: PR[] = openPRsData?.map((pr) => ({
+ totalComments: pr?.pullRequest.totalCommentsCount,
daysOpened: Math.floor(
- (CURRENT_DAY - new Date(pr.node.createdAt).getTime()) / ONE_DAY
+ (CURRENT_DAY - new Date(pr?.pullRequest.createdAt).getTime()) /
+ ONE_DAY
),
- url: pr.node.url,
- repoUrl: pr.node.repository.url,
- title: pr.node.title,
- createdAt: pr.node.createdAt,
- avatarUrl: pr.node.author.avatarUrl,
- project: pr.node.repository.owner
+ url: pr?.pullRequest.url,
+ repoUrl: pr?.pullRequest.repository.url,
+ title: pr?.pullRequest.title,
+ createdAt: pr?.pullRequest.createdAt,
+ avatarUrl: pr?.pullRequest.author.avatarUrl,
+ project: pr?.pullRequest.repository.owner
}))
- const openInactivePRsData = openPRsData.filter(
- (pr: any) =>
+ const openInactivePRsData = openPRsData?.filter(
+ (pr) =>
Math.floor(
- (CURRENT_DAY - new Date(pr.node.createdAt).getTime()) / ONE_DAY
+ (CURRENT_DAY - new Date(pr?.pullRequest.createdAt).getTime()) /
+ ONE_DAY
) > DAYS_TO_INACTIVE
)
- const openInactivePRs: PR[] = openInactivePRsData.map((pr: any) => ({
- totalComments: pr.node.totalCommentsCount,
+
+ const openInactivePRs: PR[] = openInactivePRsData?.map((pr) => ({
+ totalComments: pr?.pullRequest.totalCommentsCount,
daysOpened: Math.floor(
- (CURRENT_DAY - new Date(pr.node.createdAt).getTime()) / ONE_DAY
+ (CURRENT_DAY - new Date(pr?.pullRequest.createdAt).getTime()) /
+ ONE_DAY
),
- url: pr.node.url,
- repoUrl: pr.node.repository.url,
- title: pr.node.title,
- createdAt: pr.node.createdAt,
- avatarUrl: pr.node.author.avatarUrl,
- project: pr.node.repository.owner
+ url: pr?.pullRequest.url,
+ repoUrl: pr?.pullRequest.repository.url,
+ title: pr?.pullRequest.title,
+ createdAt: pr?.pullRequest.createdAt,
+ avatarUrl: pr?.pullRequest.author.avatarUrl,
+ project: pr?.pullRequest.repository.owner
}))
- const closedPRsData = data.pullRequests.edges.filter(
- (pr: any) => pr.node.closed === true && pr.node.merged === false
+ const closedPRsData = data?.filter(
+ (pr) =>
+ pr?.pullRequest.closed === true && pr?.pullRequest.merged === false
)
- const closedPRs: PR[] = closedPRsData.map((pr: any) => ({
- totalComments: pr.node.totalCommentsCount,
+
+ const closedPRs: PR[] = closedPRsData?.map((pr) => ({
+ totalComments: pr?.pullRequest.totalCommentsCount,
daysOpened: Math.floor(
- (new Date(pr.node.closedAt).getTime() -
- new Date(pr.node.createdAt).getTime()) /
+ (new Date(pr?.pullRequest.closedAt).getTime() -
+ new Date(pr?.pullRequest.createdAt).getTime()) /
ONE_DAY
),
- url: pr.node.url,
- repoUrl: pr.node.repository.url,
- title: pr.node.title,
- createdAt: pr.node.createdAt,
- avatarUrl: pr.node.author.avatarUrl,
- project: pr.node.repository.owner
+ url: pr?.pullRequest.url,
+ repoUrl: pr?.pullRequest.repository.url,
+ title: pr?.pullRequest.title,
+ createdAt: pr?.pullRequest.createdAt,
+ avatarUrl: pr?.pullRequest.author.avatarUrl,
+ project: pr?.pullRequest.repository.owner
}))
- const closedPRsByOthersData = data.pullRequests.edges.filter(
- (pr: any) =>
- pr.node.closed === true &&
- pr.node.merged === true &&
- pr.node.mergedBy.login !== username
+ const closedPRsByOthersData = data?.filter(
+ (pr) =>
+ pr?.pullRequest.closed === true &&
+ pr?.pullRequest.merged === true &&
+ pr?.pullRequest.mergedBy.login !== username
)
- const closedPRsByOthers: PR[] = closedPRsByOthersData.map((pr: any) => ({
- totalComments: pr.node.totalCommentsCount,
+
+ const closedPRsByOthers: PR[] = closedPRsByOthersData?.map((pr) => ({
+ totalComments: pr?.pullRequest.totalCommentsCount,
daysOpened: Math.floor(
- (new Date(pr.node.closedAt).getTime() -
- new Date(pr.node.createdAt).getTime()) /
+ (new Date(pr?.pullRequest.closedAt).getTime() -
+ new Date(pr?.pullRequest.createdAt).getTime()) /
ONE_DAY
),
- url: pr.node.url,
- repoUrl: pr.node.repository.url,
- title: pr.node.title,
- createdAt: pr.node.createdAt,
- avatarUrl: pr.node.author.avatarUrl,
- project: pr.node.repository.owner
+ url: pr?.pullRequest.url,
+ repoUrl: pr?.pullRequest.repository.url,
+ title: pr?.pullRequest.title,
+ createdAt: pr?.pullRequest.createdAt,
+ avatarUrl: pr?.pullRequest.author.avatarUrl,
+ project: pr?.pullRequest.repository.owner
}))
- const mergedPRData = data.pullRequests.edges.filter(
- (pr: any) => pr.node.merged === true
- )
- const mergedPRs: PR[] = mergedPRData.map((pr: any) => ({
- totalComments: pr.node.totalCommentsCount,
+ const mergedPRData = data?.filter((pr) => pr?.pullRequest.merged === true)
+
+ const mergedPRs: PR[] = mergedPRData?.map((pr) => ({
+ totalComments: pr?.pullRequest.totalCommentsCount,
daysOpened: Math.floor(
- (new Date(pr.node.mergedAt).getTime() -
- new Date(pr.node.createdAt).getTime()) /
+ (new Date(pr?.pullRequest.mergedAt).getTime() -
+ new Date(pr?.pullRequest.createdAt).getTime()) /
ONE_DAY
),
- url: pr.node.url,
- repoUrl: pr.node.repository.url,
- title: pr.node.title,
- createdAt: pr.node.createdAt,
- avatarUrl: pr.node.author.avatarUrl,
- project: pr.node.repository.owner
+ url: pr?.pullRequest.url,
+ repoUrl: pr?.pullRequest.repository.url,
+ title: pr?.pullRequest.title,
+ createdAt: pr?.pullRequest.createdAt,
+ avatarUrl: pr?.pullRequest.author.avatarUrl,
+ project: pr?.pullRequest.repository.owner
}))
openPRs.sort(
diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts
index f4cb415..619de2d 100644
--- a/src/helpers/utils.ts
+++ b/src/helpers/utils.ts
@@ -7,8 +7,8 @@ import {
GRID_GREEN,
GRID_YELLOW
} from "@/types"
-import { Comment, IssuesObject } from "@/types/comments"
-import { Project, PRsObject } from "@/types/pull_requests"
+import { Comment, IssueCommentNodes, IssuesObject } from "@/types/comments"
+import { PrNodes, Project, PRsObject } from "@/types/pull_requests"
export const getIssueNumber = (arg: string) => {
const splitUrl = arg.split("#")
@@ -42,24 +42,24 @@ export const collapsibleHeader = (repoUrl: string, username: string) => {
}
export const getOrganisations = (
- prsData: any,
- issueData: any,
+ prsData: PrNodes[],
+ issueData: IssueCommentNodes[],
username: string
) => {
- const prOrgs: Array = prsData?.pullRequests?.edges
- .filter(
- (pr: any) =>
- pr.node.repository.owner.login.toLowerCase() !==
- username.toLowerCase()
+ const prOrgs: Array = prsData
+ ?.filter(
+ (pr) =>
+ pr?.pullRequest?.repository.owner.login.toLowerCase() !==
+ username.toLowerCase() && pr?.pullRequest !== undefined
)
- .map((pr: any) => ({
- login: pr.node.repository.owner.login,
- avatarUrl: pr.node.repository.owner.avatarUrl
+ .map((pr) => ({
+ login: pr?.pullRequest?.repository.owner.login,
+ avatarUrl: pr?.pullRequest?.repository.owner.avatarUrl
}))
- const issueOrgs: Array = issueData?.issueComments?.nodes
- .filter(
- (issue: any) =>
+ const issueOrgs: Array = issueData
+ ?.filter(
+ (issue) =>
issue.repository.owner.login.toLowerCase() !==
username.toLowerCase()
)
@@ -68,11 +68,28 @@ export const getOrganisations = (
avatarUrl: issue.repository.owner.avatarUrl
}))
- const orgs = [...prOrgs, ...issueOrgs].filter(
- (value, index, arr) =>
- arr.map((x) => x.login).indexOf(value.login) === index
+ const allOrgs = [...prOrgs, ...issueOrgs].reduce(
+ (acc, curr) => {
+ const key = curr.login
+
+ const group = acc[key] ?? []
+ return { ...acc, [key]: [...group, curr] }
+ },
+ {} as Record>
)
+ const addOrgPosition = []
+ for (const [key, value] of Object.entries(allOrgs)) {
+ if (key) {
+ addOrgPosition.push({
+ login: value[0].login,
+ avatarUrl: value[0].avatarUrl,
+ position: value.length
+ })
+ }
+ }
+ const orgs = addOrgPosition.sort((a, c) => c.position - a.position)
+
return { orgs }
}
@@ -96,7 +113,6 @@ export const extractYears = (prsData: PRsObject, issueData: IssuesObject) => {
export function filterObject }>(
toggleFilter: string | null,
- yearlyFilter: string,
toolTipKey: string | null,
data: Type
): Type {
@@ -111,10 +127,7 @@ export function filterObject }>(
.slice(1, 3)
.join(" ")
- return (
- x.createdAt.toString().slice(0, 4) === yearlyFilter &&
- result_date === toolTipKey
- )
+ return result_date === toolTipKey
})
if (toggleFilter) {
@@ -127,8 +140,6 @@ export function filterObject }>(
.join(" ")
return (
- x.createdAt.toString().slice(0, 4) ===
- yearlyFilter &&
result_date === toolTipKey &&
x.project?.login?.toLowerCase() ===
toggleFilter?.toLowerCase()
@@ -148,30 +159,6 @@ export function filterObject }>(
x.project?.login?.toLowerCase() ===
toggleFilter?.toLowerCase()
)
-
- if (yearlyFilter) {
- for (const [key, value] of Object.entries(
- filteredObject as Type
- )) {
- filteredObject[key] = value.filter(
- (x) =>
- x.createdAt.toString().slice(0, 4) ===
- yearlyFilter &&
- x.project?.login?.toLowerCase() ===
- toggleFilter?.toLowerCase()
- )
- }
- }
- }
-
- return filteredObject
- }
-
- if (yearlyFilter) {
- for (const [key, value] of Object.entries(data)) {
- filteredObject[key] = value.filter(
- (x) => x.createdAt.toString().slice(0, 4) === yearlyFilter
- )
}
return filteredObject
@@ -195,10 +182,9 @@ export const months = [
"Dec"
]
-const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+export const days = ["Mon", "Wed", "Fri"]
export const getYearlyContributions = (
- year: string,
prsData: PRsObject,
issueData: IssuesObject
) => {
@@ -210,31 +196,23 @@ export const getYearlyContributions = (
const extractPrDates = Object.values(prsData)
.map((set) => {
- return set
- .filter(
- (item) => item.createdAt.toString().slice(0, 4) === year
- )
- .map((x) => {
- return {
- date: new Date(x.createdAt).toDateString(),
- type: "prs"
- }
- })
+ return set.map((x) => {
+ return {
+ date: new Date(x.createdAt).toDateString(),
+ type: "prs"
+ }
+ })
})
.flat()
const extractIssueDates = Object.values(issueData)
.map((set) => {
- return set
- .filter(
- (item) => item.createdAt.toString().slice(0, 4) === year
- )
- .map((x) => {
- return {
- date: new Date(x.createdAt).toDateString(),
- type: "issues"
- }
- })
+ return set.map((x) => {
+ return {
+ date: new Date(x.createdAt).toDateString(),
+ type: "issues"
+ }
+ })
})
.flat()
@@ -280,7 +258,7 @@ export const getYearlyContributions = (
})
}
- return { year, contributions }
+ return { contributions }
}
export const createGridSet = (year: string) => {
diff --git a/src/hooks/useGetYears.ts b/src/hooks/useGetYears.ts
new file mode 100644
index 0000000..02c6cc8
--- /dev/null
+++ b/src/hooks/useGetYears.ts
@@ -0,0 +1,30 @@
+import React from "react"
+import { useSearchParams } from "next/navigation"
+import { fetchYears } from "@/app/components/search/contribution-years"
+
+export const useGetYears = () => {
+ const searchParams = useSearchParams()
+ const username = searchParams.get("username")
+ const [years, setYears] = React.useState([])
+
+ React.useEffect(() => {
+ const getContributionByYear = async () => {
+ const params = {
+ username: username as string
+ }
+ const { years } = await fetchYears({
+ params
+ })
+
+ const yearsData =
+ years?.data !== undefined
+ ? years.data.map((yr) => yr.toString())
+ : []
+ setYears(yearsData)
+ }
+
+ getContributionByYear()
+ }, [username])
+
+ return { years }
+}
diff --git a/src/hooks/useGithubIssues.ts b/src/hooks/useGithubIssues.ts
index aa29032..6c89fb3 100644
--- a/src/hooks/useGithubIssues.ts
+++ b/src/hooks/useGithubIssues.ts
@@ -10,7 +10,6 @@ import { Project, PRsObject } from "@/types/pull_requests"
import { useRouter, useSearchParams } from "next/navigation"
import {
createGridSet,
- extractYears,
filterObject,
generateGraphValues,
getOrganisations,
@@ -28,7 +27,9 @@ export const useGithubIssues = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
- const [projects, setProjects] = useState>([])
+ const [projects, setProjects] = useState<
+ Array
+ >([])
const [toggleFilter, setToggleFilter] = useState("")
const [yearlyFilter, setYearlyFilter] = useState(currentYear)
const [toolTipKey, setToolTipKey] = useState("")
@@ -45,6 +46,20 @@ export const useGithubIssues = () => {
mergedPrs: []
})
+ /**
+ * We are using the Date object to create the startDate and endDate of the query range
+ * new Date(): creates a date using the Date constructor
+ * Number(args): Converts the string variable of args to a number
+ *
+ * 0, 1, 4 or 12, 0, 4: are parameters passed to the Date contructor to modify the Date object;
+ * the first Argument: This specifies the month parameter
+ * the second Argument: This specifies the day parameter
+ * the first Argument: This specifies the time parameter, using the 24:00hr clock format
+ */
+
+ const startDate = new Date(Number(yearlyFilter), 0, 1, 4).toISOString()
+ const endDate = new Date(Number(yearlyFilter), 12, 0, 4).toISOString()
+
useEffect(() => {
const fetchGithubIssues = async () => {
if (!username) {
@@ -69,29 +84,51 @@ export const useGithubIssues = () => {
mergedPrs: []
}))
setProjects([])
- const { issues, prs } = await fetchIssues({
- username: username as string
+
+ const endCursor = localStorage.getItem("end_cursor") as string
+
+ const { ranged_prs, ranged_issues } = await fetchIssues({
+ username: username as string,
+ startDate,
+ endDate,
+ endCursor
})
+ const rangedPrsData =
+ ranged_prs?.data !== undefined ? ranged_prs.data : []
+ const rangedIssuesData =
+ ranged_issues?.data !== undefined ? ranged_issues.data : []
+
+ const storedCursor =
+ Number(yearlyFilter) <= Number(currentYear) - 1
+ ? endCursor
+ : ranged_issues?.endCursorObj?.end_cursor
+
+ localStorage.setItem("end_cursor", storedCursor)
+
setLoading(false)
- if (issues.error || prs.error) {
- console.error(issues.error, "error")
- setError(issues.error[0].message || prs.error[0].message)
+ if (ranged_issues.error || ranged_prs.error) {
+ setError(
+ ranged_issues?.error[0]?.message ||
+ ranged_prs?.error[0]?.message
+ )
+ setLoading(false)
return
}
const issue = getOwnComments({
- data: issues.data,
+ data: rangedIssuesData,
username: username as string
})
const longIssue = getLongComments({
- data: issues.data
+ data: rangedIssuesData
})
const othersIssue = getOthersComments({
- data: issues.data,
+ data: rangedIssuesData,
username: username as string
})
+
const {
closedPRs,
closedPRsByOthers,
@@ -99,10 +136,14 @@ export const useGithubIssues = () => {
openInactivePRs,
openPRs
} = getPullRequests({
- data: prs.data,
+ data: rangedPrsData,
username: username as string
})
- const { orgs } = getOrganisations(prs.data, issues.data, username)
+ const { orgs } = getOrganisations(
+ rangedPrsData,
+ rangedIssuesData,
+ username
+ )
setProjects(orgs)
@@ -124,26 +165,19 @@ export const useGithubIssues = () => {
}
fetchGithubIssues()
- }, [username])
+ }, [currentYear, endDate, startDate, username, yearlyFilter])
const memoizedIssues = useMemo(
- () =>
- filterObject(toggleFilter, yearlyFilter, toolTipKey, issuesObject),
- [issuesObject, toggleFilter, toolTipKey, yearlyFilter]
+ () => filterObject(toggleFilter, toolTipKey, issuesObject),
+ [issuesObject, toggleFilter, toolTipKey]
)
const memoizedPrs = useMemo(
- () => filterObject(toggleFilter, yearlyFilter, toolTipKey, prsObject),
- [prsObject, toggleFilter, toolTipKey, yearlyFilter]
+ () => filterObject(toggleFilter, toolTipKey, prsObject),
+ [prsObject, toggleFilter, toolTipKey]
)
- const { years } = extractYears(prsObject, issuesObject)
-
- const { contributions } = getYearlyContributions(
- yearlyFilter,
- prsObject,
- issuesObject
- )
+ const { contributions } = getYearlyContributions(prsObject, issuesObject)
const { gridSet } = createGridSet(yearlyFilter!)
const memoizedGraphValues = useMemo(
@@ -156,7 +190,8 @@ export const useGithubIssues = () => {
}
const handleYearlyFilter = (key: string) => {
- setYearlyFilter((prev) => (prev === key ? prev : key))
+ if (key === yearlyFilter) return
+ setYearlyFilter(key)
}
const onClickToolTip = (content: Contribution) => {
@@ -181,8 +216,8 @@ export const useGithubIssues = () => {
toggleFilter,
yearlyFilter,
handleYearlyFilter,
- years,
memoizedGraphValues,
- onClickToolTip, goBack
+ onClickToolTip,
+ goBack
}
}
diff --git a/src/types/comments.ts b/src/types/comments.ts
index e5e881a..16677d3 100644
--- a/src/types/comments.ts
+++ b/src/types/comments.ts
@@ -1,3 +1,25 @@
+import { PageInfo, Repository } from '.'
+
+export type IssueCommentDataType = {
+ issueComments: IssueComments
+}
+
+export type IssueComments = {
+ pageInfo: PageInfo
+ nodes: Array
+}
+
+export type IssueCommentNodes = {
+ issue: Issue
+ body: string
+ repository: Repository
+ url: string
+ createdAt: string
+ author: {
+ login: string
+ }
+}
+
export type Comment = {
issue: Issue
body: string
@@ -7,7 +29,6 @@ export type Comment = {
project: { login: string; avatarUrl: string }
}
-
export type IssuesObject = {
ownIssueComments: Comment[]
longIssueComments: Comment[]
@@ -19,7 +40,5 @@ export type Issue = {
login: string
avatarUrl: string
}
- comments: {
- totalCount: number
- }
+ comments: { totalCount: number }
}
diff --git a/src/types/index.ts b/src/types/index.ts
index 6a317a0..0db39b7 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -3,6 +3,20 @@ export type Contributions = Record<
Record>
>
+export type PageInfo = {
+ hasNextPage: boolean
+ startCursor: string
+ endCursor: string
+}
+
+export type Repository = {
+ url: string
+ owner: {
+ login: string
+ avatarUrl: string
+ }
+}
+
export type GridSet = {
month: string
boxes: Array<{ day: number; is_active: boolean }>
@@ -21,3 +35,4 @@ export const GRID_YELLOW = "#E7C23E"
export const GRID_BLUE = "#0783F5"
export const GRID_GRAY = "#EEEEEE"
export const GRID_GREEN = "#39D353"
+export const MAX_COMMENT_LENGTH = 500
diff --git a/src/types/pull_requests.ts b/src/types/pull_requests.ts
index ceada8c..d1c00f4 100644
--- a/src/types/pull_requests.ts
+++ b/src/types/pull_requests.ts
@@ -1,3 +1,31 @@
+import { PageInfo, Repository } from "."
+
+export type PrDataType = {
+ contributionsCollection: {
+ contributionYears: Array
+ pullRequestContributions: {
+ pageInfo: PageInfo
+ nodes: Array
+ }
+ }
+}
+
+export type PrNodes = {
+ pullRequest: {
+ author: { avatarUrl: string }
+ closed: boolean
+ closedAt: string
+ createdAt: string
+ merged: boolean
+ mergedAt: string
+ mergedBy: { login: string }
+ repository: Repository
+ title: string
+ totalCommentsCount: number
+ url: string
+ }
+}
+
export type PR = {
totalComments: number
daysOpened: number