Skip to content

Commit

Permalink
feat: connect contributors page to API (#1113)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonroberts committed Apr 20, 2023
1 parent e9f5056 commit e51c51e
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ const CardHorizontalBarChart = ({ languageList, withDescription }: CardHorizonta
// used this state to calculate thte percentage of each language
const [percentage, setPercentage] = useState<any>(0);

const [descriptText, setDescriptText] = useState(sortedLangArray[0].languageName);
const [descriptText, setDescriptText] = useState(sortedLangArray[0]?.languageName || "javascript");

const handleChangeDescriptText = (descriptText: string) => {
setDescriptText(descriptText);
};

useEffect(() => {
if (sortedLangArray.length === 0) return;

const totalSumOfFirstFivePercentage = sortedLangArray
.slice(0, 4)
.map((lang) => lang.percentageUsed)
Expand Down
5 changes: 3 additions & 2 deletions components/molecules/PullRequestTable/pull-request-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ interface CardTableProps {
repositories?: number[];
limit?: number;
isHoverCard?: boolean;
range?: number;
}

const PullRequestTable = ({ contributor, topic, repositories, limit, isHoverCard }: CardTableProps): JSX.Element => {
const { data, isLoading } = useContributorPullRequests(contributor, topic, repositories, limit);
const PullRequestTable = ({ contributor, topic, repositories, limit, isHoverCard, range }: CardTableProps): JSX.Element => {
const { data, isLoading } = useContributorPullRequests(contributor, topic, repositories, limit, range);

return data.length > 0 ? (
<>
Expand Down
4 changes: 3 additions & 1 deletion components/molecules/RepoRow/repo-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { getAvatarByUsername } from "lib/utils/github";
import useRepositoryPullRequests from "lib/hooks/api/useRepositoryPullRequests";
import getPullRequestsToDays from "lib/utils/get-prs-to-days";
import getPullRequestsContributors from "lib/utils/get-pr-contributors";
import useStore from "lib/store";

interface RepoProps {
repo: RepositoriesRows;
Expand Down Expand Up @@ -92,7 +93,8 @@ const RepoRow = ({ repo, topic, userPage, selected, handleOnSelectRepo }: RepoPr
const ownerAvatar = getAvatarByUsername(fullName.split("/")[0]);

const { user } = useSupabaseAuth();
const { data: repositoryPullRequests } = useRepositoryPullRequests(repo.full_name, 100);
const range = useStore((state) => state.range);
const { data: repositoryPullRequests } = useRepositoryPullRequests(repo.full_name, 100, range);
const totalPrs = getTotalPrs(openPrsCount, mergedPrsCount, closedPrsCount, draftPrsCount);
const prsMergedPercentage = getPercent(totalPrs, mergedPrsCount || 0);
const spamPrsPercentage = getPrsSpam(totalPrs, spamPrsCount || 0);
Expand Down
45 changes: 34 additions & 11 deletions components/organisms/ContributorCard/contributor-card.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useState } from "react";

import { useContributorPullRequestsChart } from "lib/hooks/useContributorPullRequestsChart";

import Card from "components/atoms/Card/card";
import Text from "components/atoms/Typography/text";
import CardHorizontalBarChart, {
Expand All @@ -12,46 +10,71 @@ import CardProfile from "components/molecules/CardProfile/card-profile";
import CardRepoList, { RepoList } from "components/molecules/CardRepoList/card-repo-list";
import PullRequestTable from "components/molecules/PullRequestTable/pull-request-table";

import { useContributorPullRequestsChart } from "lib/hooks/useContributorPullRequestsChart";
import color from "lib/utils/color.json";
import { getAvatarByUsername } from "lib/utils/github";
import useRepositories from "lib/hooks/api/useRepositories";

const colorKeys = Object.keys(color);

export interface ContributorObject {
profile: {
githubAvatar: string;
githubName: string;
totalPRs: number;
dateOfFirstPR: string;
};
repoList: RepoList[];
languageList: LanguageObject[];
}

interface ContributorCardProps {
className?: string;
contributor: ContributorObject;
topic: string;
repositories?: number[];
range?: number;
}

const ContributorCard = ({ className, contributor, topic, repositories }: ContributorCardProps) => {
const { profile, repoList, languageList } = contributor;
const ContributorCard = ({ className, contributor, topic, repositories, range }: ContributorCardProps) => {
const { profile } = contributor;

const [showPRs, setShowPRs] = useState(false);
const { chart } = useContributorPullRequestsChart(profile.githubName, topic, repositories);
const { chart, data, meta } = useContributorPullRequestsChart(profile.githubName, topic, repositories, range);
const repoList: RepoList[] = Array.from(new Set(data.map(prData => prData.full_name))).map(repo => {
const [repoOwner, repoName] = repo.split("/");

return {
repoName: repoName,
repoIcon: getAvatarByUsername(repoOwner)
};
});
const repoIds = data.map(pr => pr.repo_id);
const { data: repoData } = useRepositories(repoIds);
const contributorLanguageList = Array.from(new Set(repoData.map(repo => repo.language).filter(language => !!language)));
const languageList: LanguageObject[] = contributorLanguageList
.map((language) => {
const preparedLanguageKey = colorKeys.find((key) => key.toLowerCase() === language.toLowerCase());

return {
languageName: preparedLanguageKey ? preparedLanguageKey.toLowerCase() : language,
percentageUsed: Math.round((1 / contributorLanguageList.length) * 100)
};
});

return (
<Card className={className && className}>
<div className="flex flex-col gap-3">
<div className="flex w-full justify-between items-center gap-2">
<CardProfile {...profile} />
<CardProfile {...profile} totalPRs={meta.itemCount} />
<div>
<CardHorizontalBarChart withDescription={false} languageList={languageList} />
</div>
</div>
<div className="h-[110px] overflow-hidden">
<CardLineChart lineChartOption={chart} />
</div>
<CardRepoList repoList={repoList} />
<CardRepoList repoList={repoList} total={repoList.length} />

{showPRs ? (
<PullRequestTable contributor={profile.githubName} topic={topic} repositories={repositories} />
<PullRequestTable contributor={profile.githubName} topic={topic} repositories={repositories} range={range} />
) : null}

<div className="flex w-full justify-center">
Expand Down
46 changes: 16 additions & 30 deletions components/organisms/Contributors/contributors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ import TableHeader from "components/molecules/TableHeader/table-header";
import Select from "components/atoms/Select/custom-select";

import { calcDistanceFromToday } from "lib/utils/date-utils";
import color from "lib/utils/color.json";
import { useTopicContributions } from "lib/hooks/useTopicContributions";

import ContributorCard from "../ContributorCard/contributor-card";
import SkeletonWrapper from "components/atoms/SkeletonLoader/skeleton-wrapper";

const colorKeys = Object.keys(color);
import useContributors from "lib/hooks/api/useContributors";
import { getAvatarByUsername } from "lib/utils/github";

interface ContributorProps {
repositories?: number[];
Expand All @@ -23,41 +21,28 @@ const Contributors = ({ repositories }: ContributorProps): JSX.Element => {
const router = useRouter();
const { filterName } = router.query;
const topic = filterName as string;
const { data, setLimit, meta, setPage, page, isError, isLoading } = useTopicContributions(10, repositories);
const range = useStore((state) => state.range);
const store = useStore();
const range = useStore((state) => state.range);
const { data, meta, setPage, setLimit, isError, isLoading } = useContributors(10, repositories, range);

const contributors = data.map(pr => {
return {
host_login: pr.author_login,
first_commit_time: pr.created_at
};
});

const contributorArray = isError
? []
: data?.map((contributor) => {
const timeSinceFirstCommit = calcDistanceFromToday(new Date(parseInt(contributor.first_commit_time)));
const contributorLanguageList = (contributor.langs || "").split(",");
const repoList = (contributor.recent_repo_list || "").split(",").map((repo) => {
const [repoOwner, repoName] = repo.split("/");

return {
repoName,
repoIcon: `https://www.github.com/${repoOwner ?? "github"}.png?size=460`
};
});
const languageList = contributorLanguageList.map((language) => {
const preparedLanguageKey = colorKeys.find((key) => key.toLowerCase() === language.toLowerCase());

return {
languageName: preparedLanguageKey ? preparedLanguageKey : language,
percentageUsed: Math.round((1 / contributorLanguageList.length) * 100)
};
});
: contributors.map((contributor) => {
const timeSinceFirstCommit = calcDistanceFromToday(new Date(contributor.first_commit_time));

return {
profile: {
githubAvatar: `https://www.github.com/${contributor.host_login}.png?size=60`,
githubAvatar: getAvatarByUsername(contributor.host_login),
githubName: contributor.host_login,
totalPRs: contributor.recent_pr_total,
dateOfFirstPR: timeSinceFirstCommit
},
languageList,
repoList
}
};
});

Expand All @@ -83,6 +68,7 @@ const Contributors = ({ repositories }: ContributorProps): JSX.Element => {
contributor={{ ...contributor }}
topic={topic}
repositories={repositories}
range={range}
/>
))}
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/organisms/Dashboard/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface DashboardProps {

const Dashboard = ({ repositories }: DashboardProps): JSX.Element => {
const { data: insightsData, isLoading } = useInsights(repositories);
const { data: prData, isError: prError } = usePullRequests(undefined, repositories);
const { data: prData, meta: prMeta, isError: prError } = usePullRequests(undefined, repositories);
const [showBots, setShowBots] = useState(false);
const isMobile = useMediaQuery("(max-width:720px)");
const [prStateFilter, setPrStateFilter] = useState<PrStatusFilter>("all");
Expand Down Expand Up @@ -99,7 +99,7 @@ const Dashboard = ({ repositories }: DashboardProps): JSX.Element => {
metricIncreases={compare1.allContributors - compare2.allContributors >= 0}
increased={compare1.allContributors - compare2.allContributors >= 0}
numChanged={humanizeNumber(Math.abs(compare1.allContributors - compare2.allContributors), "abbreviation")}
value={humanizeNumber(compare1.allContributors, "comma")}
value={humanizeNumber(prMeta.itemCount, "comma")}
contributors={contributorData}
isLoading={isLoading}
/>
Expand Down
4 changes: 2 additions & 2 deletions components/organisms/Repositories/repositories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import TableHeader from "components/molecules/TableHeader/table-header";

import useRepositories from "lib/hooks/api/useRepositories";
import useSupabaseAuth from "lib/hooks/useSupabaseAuth";
import useStore from "lib/store";

import RepositoriesTable, { classNames, RepositoriesRows } from "../RepositoriesTable/repositories-table";
import RepoNotIndexed from "./repository-not-indexed";
import Checkbox from "components/atoms/Checkbox/checkbox";
import Button from "components/atoms/Button/button";
import useStore from "lib/store";

interface RepositoriesProps {
repositories?: number[];
Expand All @@ -36,7 +36,7 @@ const Repositories = ({ repositories }: RepositoriesProps): JSX.Element => {
isLoading: repoListIsLoading,
setPage,
setLimit
} = useRepositories(repositories);
} = useRepositories(repositories, range);
const filteredRepoNotIndexed = selectedFilter && !repoListIsLoading && !repoListIsError && repoListData.length === 0;
const [selectedRepos, setSelectedRepos] = useState<DbRepo[]>([]);

Expand Down
69 changes: 69 additions & 0 deletions lib/hooks/api/useContributors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useState } from "react";
import useSWR, { Fetcher } from "swr";
import { useRouter } from "next/router";

import publicApiFetcher from "lib/utils/public-api-fetcher";
import getFilterQuery from "lib/utils/get-filter-query";

interface PaginatedResponse {
readonly data: DbRepoPR[];
readonly meta: Meta;
}

/**
* Fetch contributors based on pull requests.
* Replace with contributors API endpoint when available.
*
* @param intialLimit
* @param repoIds
* @param range
* @returns
*/
const useContributors = (intialLimit = 10, repoIds: number[] = [], range = 30) => {
const router = useRouter();
const [page, setPage] = useState(1);
const [limit, setLimit] = useState(intialLimit);
const { filterName, selectedFilter } = router.query;
const topic = filterName as string;
const filterQuery = getFilterQuery(selectedFilter);
const query = new URLSearchParams(filterQuery);

if (Number.isNaN(Number(topic))) {
query.set("topic", topic);
}

if (page) {
query.set("page", `${page}`);
}

if (limit) {
query.set("limit", `${limit}`);
}

if (repoIds?.length > 0) {
query.set("repoIds", repoIds.join(","));
}

query.set("range", `${range}`);

const baseEndpoint = "prs/search";
const endpointString = `${baseEndpoint}?${query.toString()}`;

const { data, error, mutate } = useSWR<PaginatedResponse, Error>(
endpointString,
publicApiFetcher as Fetcher<PaginatedResponse, Error>
);

return {
data: data?.data ?? [],
meta: data?.meta ?? { itemCount: 0, limit: 0, page: 0, hasNextPage: false, hasPreviousPage: false, pageCount: 0 },
isLoading: !error && !data,
isError: !!error,
mutate,
page,
setPage,
setLimit
};
};

export default useContributors;
8 changes: 6 additions & 2 deletions lib/hooks/api/usePullRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ interface PaginatedResponse {
readonly meta: Meta;
}

const usePullRequests = (limit = 1000, repoIds: number[] = []) => {
const usePullRequests = (intialLimit = 1000, repoIds: number[] = [], range = 30) => {
const router = useRouter();
const [page, setPage] = useState(1);
const [limit, setLimit] = useState(intialLimit);
const { filterName, selectedFilter } = router.query;
const topic = filterName as string;
const filterQuery = getFilterQuery(selectedFilter);
Expand All @@ -34,6 +35,8 @@ const usePullRequests = (limit = 1000, repoIds: number[] = []) => {
query.set("repoIds", repoIds.join(","));
}

query.set("range", `${range}`);

const baseEndpoint = "prs/search";
const endpointString = `${baseEndpoint}?${query.toString()}`;

Expand All @@ -49,7 +52,8 @@ const usePullRequests = (limit = 1000, repoIds: number[] = []) => {
isError: !!error,
mutate,
page,
setPage
setPage,
setLimit
};
};

Expand Down
6 changes: 5 additions & 1 deletion lib/hooks/api/useRepositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface PaginatedResponse {
readonly meta: Meta;
}

const useRepositories = (repoIds: number[] = []) => {
const useRepositories = (repoIds: number[] = [], range = 30) => {
const router = useRouter();
const [page, setPage] = useState(1);
const [limit, setLimit] = useState(10);
Expand All @@ -31,6 +31,10 @@ const useRepositories = (repoIds: number[] = []) => {
query.set("limit", `${limit}`);
}

if (range) {
query.set("range", `${range}`);
}

if (repoIds?.length > 0) {
query.set("repoIds", repoIds.join(","));
}
Expand Down
3 changes: 2 additions & 1 deletion lib/hooks/api/useRepositoryPullRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface PaginatedResponse {
readonly meta: Meta;
}

const useRepositoryPullRequests = (fullName: string, limit = 10) => {
const useRepositoryPullRequests = (fullName: string, limit = 10, range = 30) => {
const router = useRouter();
const { filterName } = router.query;
const topic = filterName as string;
Expand All @@ -21,6 +21,7 @@ const useRepositoryPullRequests = (fullName: string, limit = 10) => {
query.set("repo", fullName);
query.set("page", "1");
query.set("limit", `${limit}`);
query.set("range", `${range}`);

const baseEndpoint = "prs/search";
const endpointString = `${baseEndpoint}?${query.toString()}`;
Expand Down
Loading

0 comments on commit e51c51e

Please sign in to comment.