Use branches from GitHub API in edit domain dialog (#74)
* Use branches from GitHub API * Disable git branch input if repo not found * Disable git branch if branches is empty * Use async select for accounts dropdown * Log actual HTTP error in console --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
parent
ce55fe62d8
commit
c3d1b4f3eb
@ -3,11 +3,12 @@ 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, Select } from '@material-tailwind/react';
|
import { Button, Typography, Option } from '@material-tailwind/react';
|
||||||
|
|
||||||
import SearchBar from '../../SearchBar';
|
import SearchBar from '../../SearchBar';
|
||||||
import ProjectRepoCard from './ProjectRepoCard';
|
import ProjectRepoCard from './ProjectRepoCard';
|
||||||
import { GitOrgDetails, GitRepositoryDetails } from '../../../types';
|
import { GitOrgDetails, GitRepositoryDetails } from '../../../types';
|
||||||
|
import AsyncSelect from '../../shared/AsyncSelect';
|
||||||
|
|
||||||
const DEFAULT_SEARCHED_REPO = '';
|
const DEFAULT_SEARCHED_REPO = '';
|
||||||
const REPOS_PER_PAGE = 5;
|
const REPOS_PER_PAGE = 5;
|
||||||
@ -109,8 +110,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="flex gap-2 mb-2">
|
<div className="flex gap-2 mb-2">
|
||||||
<div className="basis-1/3">
|
<div className="basis-1/3">
|
||||||
{/* TODO: Fix selection of Git user at start */}
|
<AsyncSelect
|
||||||
<Select
|
|
||||||
value={selectedAccount}
|
value={selectedAccount}
|
||||||
onChange={(value) => setSelectedAccount(value!)}
|
onChange={(value) => setSelectedAccount(value!)}
|
||||||
>
|
>
|
||||||
@ -119,7 +119,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
|
|||||||
^ {account.login}
|
^ {account.login}
|
||||||
</Option>
|
</Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</AsyncSelect>
|
||||||
</div>
|
</div>
|
||||||
<div className="basis-2/3">
|
<div className="basis-2/3">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
|
@ -12,7 +12,6 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
|
|
||||||
import { RepositoryDetails } from '../../../../types';
|
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
||||||
import EditDomainDialog from './EditDomainDialog';
|
import EditDomainDialog from './EditDomainDialog';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../context/GQLClientContext';
|
||||||
@ -27,7 +26,7 @@ enum RefreshStatus {
|
|||||||
interface DomainCardProps {
|
interface DomainCardProps {
|
||||||
domains: Domain[];
|
domains: Domain[];
|
||||||
domain: Domain;
|
domain: Domain;
|
||||||
repo: RepositoryDetails;
|
branches: string[];
|
||||||
project: Project;
|
project: Project;
|
||||||
onUpdate: () => Promise<void>;
|
onUpdate: () => Promise<void>;
|
||||||
}
|
}
|
||||||
@ -44,7 +43,7 @@ const DOMAIN_RECORD = {
|
|||||||
const DomainCard = ({
|
const DomainCard = ({
|
||||||
domains,
|
domains,
|
||||||
domain,
|
domain,
|
||||||
repo,
|
branches,
|
||||||
project,
|
project,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
}: DomainCardProps) => {
|
}: DomainCardProps) => {
|
||||||
@ -188,7 +187,7 @@ const DomainCard = ({
|
|||||||
domains={domains}
|
domains={domains}
|
||||||
open={editDialogOpen}
|
open={editDialogOpen}
|
||||||
domain={domain}
|
domain={domain}
|
||||||
repo={repo}
|
branches={branches}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -15,7 +15,6 @@ import {
|
|||||||
Option,
|
Option,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
|
|
||||||
import { RepositoryDetails } from '../../../../types';
|
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../context/GQLClientContext';
|
||||||
|
|
||||||
const DEFAULT_REDIRECT_OPTIONS = ['none'];
|
const DEFAULT_REDIRECT_OPTIONS = ['none'];
|
||||||
@ -25,7 +24,7 @@ interface EditDomainDialogProp {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
handleOpen: () => void;
|
handleOpen: () => void;
|
||||||
domain: Domain;
|
domain: Domain;
|
||||||
repo: RepositoryDetails;
|
branches: string[];
|
||||||
onUpdate: () => Promise<void>;
|
onUpdate: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ const EditDomainDialog = ({
|
|||||||
open,
|
open,
|
||||||
handleOpen,
|
handleOpen,
|
||||||
domain,
|
domain,
|
||||||
repo,
|
branches,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
}: EditDomainDialogProp) => {
|
}: EditDomainDialogProp) => {
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
@ -120,7 +119,7 @@ const EditDomainDialog = ({
|
|||||||
branch: domain.branch,
|
branch: domain.branch,
|
||||||
redirectedTo: getRedirectUrl(domain),
|
redirectedTo: getRedirectUrl(domain),
|
||||||
});
|
});
|
||||||
}, [domain, repo]);
|
}, [domain]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} handler={handleOpen}>
|
<Dialog open={open} handler={handleOpen}>
|
||||||
@ -166,9 +165,13 @@ const EditDomainDialog = ({
|
|||||||
<Input
|
<Input
|
||||||
crossOrigin={undefined}
|
crossOrigin={undefined}
|
||||||
{...register('branch', {
|
{...register('branch', {
|
||||||
validate: (value) => repo.branch.includes(value),
|
validate: (value) =>
|
||||||
|
Boolean(branches.length) ? branches.includes(value) : true,
|
||||||
})}
|
})}
|
||||||
disabled={watch('redirectedTo') !== DEFAULT_REDIRECT_OPTIONS[0]}
|
disabled={
|
||||||
|
!Boolean(branches.length) ||
|
||||||
|
watch('redirectedTo') !== DEFAULT_REDIRECT_OPTIONS[0]
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{!isValid && (
|
{!isValid && (
|
||||||
<Typography variant="small" className="text-red-500">
|
<Typography variant="small" className="text-red-500">
|
||||||
|
@ -14,13 +14,13 @@ import { useGQLClient } from './GQLClientContext';
|
|||||||
const UNAUTHORIZED_ERROR_CODE = 401;
|
const UNAUTHORIZED_ERROR_CODE = 401;
|
||||||
|
|
||||||
interface ContextValue {
|
interface ContextValue {
|
||||||
octokit: Octokit | null;
|
octokit: Octokit;
|
||||||
isAuth: boolean;
|
isAuth: boolean;
|
||||||
updateAuth: () => void;
|
updateAuth: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OctokitContext = createContext<ContextValue>({
|
const OctokitContext = createContext<ContextValue>({
|
||||||
octokit: null,
|
octokit: new Octokit(),
|
||||||
isAuth: false,
|
isAuth: false,
|
||||||
updateAuth: () => {},
|
updateAuth: () => {},
|
||||||
});
|
});
|
||||||
|
@ -24,7 +24,7 @@ const NewProject = () => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<h5 className="mt-4 ml-4">Import a repository</h5>
|
<h5 className="mt-4 ml-4">Import a repository</h5>
|
||||||
<RepositoryList octokit={octokit!} />
|
<RepositoryList octokit={octokit} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ConnectAccount onAuth={updateAuth} />
|
<ConnectAccount onAuth={updateAuth} />
|
||||||
|
@ -24,10 +24,6 @@ const OverviewTabPanel = () => {
|
|||||||
const { project } = useOutletContext<OutletContextType>();
|
const { project } = useOutletContext<OutletContextType>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!octokit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Save repo commits in DB and avoid using GitHub API in frontend
|
// TODO: Save repo commits in DB and avoid using GitHub API in frontend
|
||||||
// TODO: Figure out fetching latest commits for all branches
|
// TODO: Figure out fetching latest commits for all branches
|
||||||
const fetchRepoActivity = async () => {
|
const fetchRepoActivity = async () => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import { RequestError } from 'octokit';
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { Link, useOutletContext } from 'react-router-dom';
|
import { Link, useOutletContext } from 'react-router-dom';
|
||||||
import { Domain } from 'gql-client';
|
import { Domain } from 'gql-client';
|
||||||
|
|
||||||
@ -6,14 +7,41 @@ import { Button, Typography } from '@material-tailwind/react';
|
|||||||
|
|
||||||
import DomainCard from '../../../../../components/projects/project/settings/DomainCard';
|
import DomainCard from '../../../../../components/projects/project/settings/DomainCard';
|
||||||
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
||||||
import repositories from '../../../../../assets/repositories.json';
|
|
||||||
import { OutletContextType } from '../../../../../types';
|
import { OutletContextType } from '../../../../../types';
|
||||||
|
import { useOctokit } from '../../../../../context/OctokitContext';
|
||||||
|
|
||||||
const Domains = () => {
|
const Domains = () => {
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
|
const { octokit } = useOctokit();
|
||||||
const { project } = useOutletContext<OutletContextType>();
|
const { project } = useOutletContext<OutletContextType>();
|
||||||
|
|
||||||
const [domains, setDomains] = useState<Domain[]>([]);
|
const [domains, setDomains] = useState<Domain[]>([]);
|
||||||
|
const [branches, setBranches] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const fetchBranches = useCallback(async () => {
|
||||||
|
const [owner, repo] = project.repository.split('/');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await octokit.rest.repos.listBranches({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
});
|
||||||
|
|
||||||
|
const branches = result.data.map((repo) => repo.name);
|
||||||
|
|
||||||
|
setBranches(branches);
|
||||||
|
} catch (err) {
|
||||||
|
if (!(err instanceof RequestError && err.status === 404)) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchBranches();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const fetchDomains = async () => {
|
const fetchDomains = async () => {
|
||||||
if (project === undefined) {
|
if (project === undefined) {
|
||||||
@ -46,7 +74,7 @@ const Domains = () => {
|
|||||||
domain={domain}
|
domain={domain}
|
||||||
key={domain.id}
|
key={domain.id}
|
||||||
// TODO: Use github API for getting linked repository
|
// TODO: Use github API for getting linked repository
|
||||||
repo={repositories[0]!}
|
branches={branches}
|
||||||
project={project}
|
project={project}
|
||||||
onUpdate={fetchDomains}
|
onUpdate={fetchDomains}
|
||||||
/>
|
/>
|
||||||
|
@ -6,15 +6,6 @@ export interface GitOrgDetails {
|
|||||||
avatar_url: string;
|
avatar_url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use GitRepositoryDetails
|
|
||||||
export interface RepositoryDetails {
|
|
||||||
title: string;
|
|
||||||
updatedAt: string;
|
|
||||||
user: string;
|
|
||||||
private: boolean;
|
|
||||||
branch: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GitRepositoryDetails {
|
export interface GitRepositoryDetails {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user