Skip to content

Commit

Permalink
feat: add repository filtering to contributor insight activity page (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonroberts committed May 9, 2024
1 parent 0d3d38d commit 58f6dda
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 17 deletions.
11 changes: 10 additions & 1 deletion layouts/lists.tsx
Expand Up @@ -6,6 +6,8 @@ import ListHeader from "components/ListHeader/list-header";
import TabsList from "components/TabList/tab-list";
import ComponentDateFilter from "components/molecules/ComponentDateFilter/component-date-filter";
import { setQueryParams } from "lib/utils/query-params";
import TrackedRepositoryFilter from "components/Workspaces/TrackedRepositoryFilter";
import { OptionKeys } from "components/atoms/Select/multi-select";

const ListPageLayout = ({
children,
Expand All @@ -15,6 +17,9 @@ const ListPageLayout = ({
showRangeFilter = true,
workspaceId,
owners,
repoFilter = false,
repoFilterOptions = [],
repoFilterSelect = () => {},
}: {
children: React.ReactNode;
list?: DBList;
Expand All @@ -23,6 +28,9 @@ const ListPageLayout = ({
isOwner: boolean;
showRangeFilter?: boolean;
owners?: string[];
repoFilter?: boolean;
repoFilterOptions?: OptionKeys[];
repoFilterSelect?: (repo: OptionKeys[]) => void;
}) => {
const router = useRouter();
const { range } = router.query;
Expand Down Expand Up @@ -61,13 +69,14 @@ const ListPageLayout = ({
/>
)}
<div>
<div className="flex justify-end p-4 md:p-0">
<div className="flex justify-end items-center gap-4">
{showRangeFilter && (
<ComponentDateFilter
defaultRange={Number(range ?? 30)}
setRangeFilter={(range) => setQueryParams({ range: `${range}` })}
/>
)}
{repoFilter && <TrackedRepositoryFilter options={repoFilterOptions} handleSelect={repoFilterSelect} />}
</div>
</div>
</div>
Expand Down
6 changes: 6 additions & 0 deletions lib/hooks/api/useContributionsByEvolutionType.ts
Expand Up @@ -13,17 +13,23 @@ const useContributionsEvolutionByType = ({
listId,
range = 30,
defaultContributorType = "all",
repos,
}: {
listId: string;
range: number;
defaultContributorType?: ContributorType;
repos?: string[];
}) => {
const [contributorType, setContributorType] = useState<ContributorType>(defaultContributorType);

const query = new URLSearchParams();
query.set("contributorType", `${contributorType}`);
query.set("range", `${range}`);

if (repos && repos.length > 0) {
query.set("repos", repos.join(","));
}

const apiEndpoint = `lists/${listId}/stats/contributions-evolution-by-contributor-type?${query.toString()}`;

const { data, error, mutate } = useSWR<ContributionEvolutionByTypeDatum[], Error>(
Expand Down
7 changes: 6 additions & 1 deletion lib/hooks/api/useContributorsByProject.ts
Expand Up @@ -2,14 +2,19 @@ import useSWR, { Fetcher } from "swr";
import { useState } from "react";
import { publicApiFetcher } from "lib/utils/public-api-fetcher";

export const useContributorsByProject = (listId: string, range = 30) => {
export const useContributorsByProject = (listId: string, range = 30, repos?: string[]) => {
const [repoName, setRepoName] = useState<string | null>(null);

const query = new URLSearchParams();

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

if (repos && repos.length > 0) {
query.set("repos", repos.join(","));
}

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

const baseEndpoint = `lists/${listId}/stats/top-project-contributions-by-contributor`;
Expand Down
8 changes: 7 additions & 1 deletion lib/hooks/api/useMostActiveContributors.ts
Expand Up @@ -20,12 +20,14 @@ const useMostActiveContributors = ({
initData,
intialLimit = 20,
defaultContributorType = "all",
repos,
}: {
listId: string;
initData?: ContributorStat[];
intialLimit?: number;

defaultContributorType?: ContributorType;
repos?: string[];
}) => {
const [limit, setLimit] = useState(intialLimit);
const router = useRouter();
Expand All @@ -40,10 +42,14 @@ const useMostActiveContributors = ({
query.set("orderBy", "total_contributions");
query.set("orderDirection", "DESC");

if (repos) {
query.set("repos", repos.join(","));
}

const apiEndpoint = `lists/${listId}/stats/most-active-contributors?${query.toString()}`;

const { data, error, mutate } = useSWR<PaginatedResponse, Error>(
listId ? apiEndpoint : null,
listId && repos ? apiEndpoint : null,
expPublicApiFetcher as Fetcher<PaginatedResponse, Error>
);

Expand Down
Expand Up @@ -2,7 +2,7 @@ import { createPagesServerClient } from "@supabase/auth-helpers-nextjs";
import { GetServerSidePropsContext } from "next";
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { useState } from "react";
import { useEffect, useState } from "react";
import { NodeMouseEventHandler } from "@nivo/treemap";
import { useRef } from "react";
import ContributionsEvolutionByType from "components/molecules/ContributionsEvolutionByTypeCard/contributions-evolution-by-type-card";
Expand All @@ -22,6 +22,7 @@ import { OnToggleResizeEventType } from "components/Graphs/shared/graph-resizer"
import { WorkspaceLayout } from "components/Workspaces/WorkspaceLayout";
import { useIsWorkspaceUpgraded } from "lib/hooks/api/useIsWorkspaceUpgraded";
import WorkspaceBanner from "components/Workspaces/WorkspaceBanner";
import { OptionKeys } from "components/atoms/Select/multi-select";

const InsightUpgradeModal = dynamic(() => import("components/Workspaces/InsightUpgradeModal"));

Expand Down Expand Up @@ -177,32 +178,38 @@ const ListActivityPage = ({
}: ContributorListPageProps) => {
const router = useRouter();
const range = router.query.range as string;
const {
data: projectData = [],
error: projectDataError,
isLoading: isTreemapLoading,
} = useContributionsByProject({
listId: list!.id,
range: Number(range ?? "30"),
});
const [filteredRepositories, setFilteredRepositories] = useState<OptionKeys[]>(
projectData.map((pd) => ({ label: pd.repo_name, value: pd.repo_name }))
);
const filteredRepos = filteredRepositories.map(({ label }) => label);
const filterOptions = projectData.map(({ repo_name }) => {
return { label: repo_name, value: repo_name };
});
const {
data: contributorStats,
isLoading,
isError: isMostActiveError,
setContributorType,
contributorType,
} = useMostActiveContributors({ listId: list!.id });
} = useMostActiveContributors({ listId: list!.id, repos: filteredRepos });
const [currentOrgName, setCurrentOrgName] = useState<string | null>(null);
const {
setRepoName,
error,
data: projectContributionsByUser,
repoName,
isLoading: isLoadingProjectContributionsByUser,
} = useContributorsByProject(list!.id, Number(range ?? "30"));
} = useContributorsByProject(list!.id, Number(range ?? "30"), filteredRepos);
const [projectId, setProjectId] = useState<string | null>(null);

const {
data: projectData = [],
error: projectDataError,
isLoading: isTreemapLoading,
} = useContributionsByProject({
listId: list!.id,
range: Number(range ?? "30"),
});

const onDrillDown: NodeMouseEventHandler<{ orgName: null; repoName: null; id: string | null }> = (node) => {
if (currentOrgName === null) {
setCurrentOrgName(node.data.orgName);
Expand All @@ -221,14 +228,24 @@ const ListActivityPage = ({
setCurrentOrgName(null);
}
};
const filteredProjectData = filteredRepos.length
? projectData.filter(({ repo_name }) => {
return filteredRepos.includes(repo_name);
})
: projectData;

const treemapData = getTreemapData({ currentOrgName, repoName, projectData, projectContributionsByUser });
const treemapData = getTreemapData({
currentOrgName,
repoName,
projectData: filteredProjectData,
projectContributionsByUser,
});

const {
data: evolutionData,
isError: evolutionError,
isLoading: isLoadingEvolution,
} = useContributionsEvolutionByType({ listId: list!.id, range: Number(range ?? "30") });
} = useContributionsEvolutionByType({ listId: list!.id, range: Number(range ?? "30"), repos: filteredRepos });
const treemapRef = useRef<HTMLSpanElement>(null);
const mostActiveRef = useRef<HTMLSpanElement>(null);
const graphResizerLookup = new Map();
Expand All @@ -255,6 +272,10 @@ const ListActivityPage = ({
treemap.style.gridRow = checked ? "1 / span 2" : "";
};

useEffect(() => {
setFilteredRepositories(projectData.map(({ repo_name }) => ({ label: repo_name, value: repo_name })));
}, [projectData]);

return (
<WorkspaceLayout
workspaceId={workspaceId}
Expand All @@ -270,6 +291,9 @@ const ListActivityPage = ({
numberOfContributors={numberOfContributors}
isOwner={isOwner}
owners={owners}
repoFilter={true}
repoFilterOptions={filterOptions}
repoFilterSelect={setFilteredRepositories}
>
{isError ? (
<Error errorMessage="Unable to load list activity" />
Expand Down

0 comments on commit 58f6dda

Please sign in to comment.