♻️ refactor: restructured and restryling repository list component

This commit is contained in:
Wahyu Kurniawan 2024-02-28 21:16:26 +07:00
parent b1bf47d104
commit 99eb514306
No known key found for this signature in database
GPG Key ID: 040A1549143A8E33
3 changed files with 61 additions and 40 deletions

View File

@ -3,13 +3,17 @@ import { Octokit } from 'octokit';
import assert from 'assert'; import assert from 'assert';
import { useDebounce } from 'usehooks-ts'; import { useDebounce } from 'usehooks-ts';
import { Button, Typography, Option } from '@material-tailwind/react'; import { Button, Typography } from '@material-tailwind/react';
import SearchBar from '../../SearchBar'; import { ProjectRepoCard } from 'components/projects/create/ProjectRepoCard';
import ProjectRepoCard from './ProjectRepoCard'; import { GitOrgDetails, GitRepositoryDetails } from 'types';
import { GitOrgDetails, GitRepositoryDetails } from '../../../types'; import {
import AsyncSelect from '../../shared/AsyncSelect'; ChevronGrabberHorizontal,
import { GithubIcon } from 'components/shared/CustomIcon'; GithubIcon,
SearchIcon,
} from 'components/shared/CustomIcon';
import { Select, SelectOption } from 'components/shared/Select';
import { Input } from 'components/shared/Input';
const DEFAULT_SEARCHED_REPO = ''; const DEFAULT_SEARCHED_REPO = '';
const REPOS_PER_PAGE = 5; const REPOS_PER_PAGE = 5;
@ -18,9 +22,9 @@ interface RepositoryListProps {
octokit: Octokit; octokit: Octokit;
} }
const RepositoryList = ({ octokit }: RepositoryListProps) => { export const RepositoryList = ({ octokit }: RepositoryListProps) => {
const [searchedRepo, setSearchedRepo] = useState(DEFAULT_SEARCHED_REPO); const [searchedRepo, setSearchedRepo] = useState(DEFAULT_SEARCHED_REPO);
const [selectedAccount, setSelectedAccount] = useState(''); const [selectedAccount, setSelectedAccount] = useState<SelectOption>();
const [orgs, setOrgs] = useState<GitOrgDetails[]>([]); const [orgs, setOrgs] = useState<GitOrgDetails[]>([]);
// TODO: Add new type for Git user when required // TODO: Add new type for Git user when required
const [gitUser, setGitUser] = useState<GitOrgDetails>(); const [gitUser, setGitUser] = useState<GitOrgDetails>();
@ -35,7 +39,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
const orgs = await octokit.rest.orgs.listForAuthenticatedUser(); const orgs = await octokit.rest.orgs.listForAuthenticatedUser();
setOrgs(orgs.data); setOrgs(orgs.data);
setGitUser(user.data); setGitUser(user.data);
setSelectedAccount(user.data.login); setSelectedAccount({ label: user.data.login, value: user.data.login });
}; };
fetchUserAndOrgs(); fetchUserAndOrgs();
@ -54,7 +58,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
let query = `${debouncedSearchedRepo} in:name fork:true`; let query = `${debouncedSearchedRepo} in:name fork:true`;
// Check if selected account is an organization // Check if selected account is an organization
if (selectedAccount === gitUser.login) { if (selectedAccount.value === gitUser.login) {
query = query + ` user:${selectedAccount}`; query = query + ` user:${selectedAccount}`;
} else { } else {
query = query + ` org:${selectedAccount}`; query = query + ` org:${selectedAccount}`;
@ -69,7 +73,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
return; return;
} }
if (selectedAccount === gitUser.login) { if (selectedAccount.value === gitUser.login) {
const result = await octokit.rest.repos.listForAuthenticatedUser({ const result = await octokit.rest.repos.listForAuthenticatedUser({
per_page: REPOS_PER_PAGE, per_page: REPOS_PER_PAGE,
affiliation: 'owner', affiliation: 'owner',
@ -78,7 +82,9 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
return; return;
} }
const selectedOrg = orgs.find((org) => org.login === selectedAccount); const selectedOrg = orgs.find(
(org) => org.login === selectedAccount.value,
);
assert(selectedOrg, 'Selected org not found in list'); assert(selectedOrg, 'Selected org not found in list');
const result = await octokit.rest.repos.listForOrg({ const result = await octokit.rest.repos.listForOrg({
@ -96,7 +102,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
const handleResetFilters = useCallback(() => { const handleResetFilters = useCallback(() => {
assert(gitUser, 'Git user is not available'); assert(gitUser, 'Git user is not available');
setSearchedRepo(DEFAULT_SEARCHED_REPO); setSearchedRepo(DEFAULT_SEARCHED_REPO);
setSelectedAccount(gitUser.login); setSelectedAccount({ label: gitUser.login, value: gitUser.login });
}, [gitUser]); }, [gitUser]);
const accounts = useMemo(() => { const accounts = useMemo(() => {
@ -107,35 +113,51 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
return [gitUser, ...orgs]; return [gitUser, ...orgs];
}, [octokit, orgs, gitUser]); }, [octokit, orgs, gitUser]);
const options = useMemo(() => {
return accounts.map((account) => ({
label: account.login,
value: account.login,
leftIcon: <GithubIcon />,
}));
}, [accounts]);
return ( return (
<div className="p-4"> <section className="space-y-3">
<div className="flex gap-2 mb-2 items-center"> {/* Dropdown and search */}
<div className="basis-1/3"> <div className="flex flex-col lg:flex-row gap-0 lg:gap-3 items-center">
<AsyncSelect <div className="lg:basis-1/3 w-full">
<Select
options={options}
placeholder="Select a repository"
value={selectedAccount} value={selectedAccount}
onChange={(value) => setSelectedAccount(value!)} rightIcon={<ChevronGrabberHorizontal />}
> onChange={(value) => setSelectedAccount(value as SelectOption)}
{accounts.map((account) => ( />
<Option key={account.id} value={account.login}>
<div className="flex items-center gap-2 justify-start">
<GithubIcon /> {account.login}
</div>
</Option>
))}
</AsyncSelect>
</div> </div>
<div className="basis-2/3 flex-grow flex items-center"> <div className="basis-2/3 flex w-full flex-grow">
<SearchBar <Input
className="w-full"
value={searchedRepo} value={searchedRepo}
onChange={(event) => setSearchedRepo(event.target.value)}
placeholder="Search for repository" placeholder="Search for repository"
leftIcon={<SearchIcon />}
onChange={(e) => setSearchedRepo(e.target.value)}
/> />
</div> </div>
</div> </div>
{/* Repository list */}
{Boolean(repositoryDetails.length) ? ( {Boolean(repositoryDetails.length) ? (
repositoryDetails.map((repo, key) => { <div className="flex flex-col gap-2">
return <ProjectRepoCard repository={repo} key={key} />; {repositoryDetails.map((repo, index) => (
}) <>
<ProjectRepoCard repository={repo} key={index} />
{/* Horizontal line */}
{index !== repositoryDetails.length - 1 && (
<div className="border-b border-border-separator/[0.06] w-full" />
)}
</>
))}
</div>
) : ( ) : (
<div className="mt-4 p-6 flex items-center justify-center"> <div className="mt-4 p-6 flex items-center justify-center">
<div className="text-center"> <div className="text-center">
@ -151,8 +173,6 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
</div> </div>
</div> </div>
)} )}
</div> </section>
); );
}; };
export default RepositoryList;

View File

@ -0,0 +1 @@
export * from './RepositoryList';

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import templates from 'assets/templates'; import templates from 'assets/templates';
import RepositoryList from 'components/projects/create/RepositoryList'; import { RepositoryList } from 'components/projects/create/RepositoryList';
import ConnectAccount from 'components/projects/create/ConnectAccount'; import ConnectAccount from 'components/projects/create/ConnectAccount';
import { useOctokit } from 'context/OctokitContext'; import { useOctokit } from 'context/OctokitContext';
import { Heading } from 'components/shared/Heading'; import { Heading } from 'components/shared/Heading';
@ -13,8 +13,8 @@ const NewProject = () => {
return isAuth ? ( return isAuth ? (
<> <>
<div className="space-y-3"> <div className="space-y-3">
<Heading as="h3" className="font-medium text-lg"> <Heading as="h3" className="font-medium text-lg pl-1">
Start with template Start with a template
</Heading> </Heading>
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3"> <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3">
{templates.map((template) => { {templates.map((template) => {
@ -28,7 +28,7 @@ const NewProject = () => {
})} })}
</div> </div>
</div> </div>
<Heading as="h3" className="font-medium text-lg mt-10"> <Heading as="h3" className="font-medium text-lg mt-10 pl-1 mb-3">
Import a repository Import a repository
</Heading> </Heading>
<RepositoryList octokit={octokit} /> <RepositoryList octokit={octokit} />