Skip to content

Commit

Permalink
Merge pull request #54 from gimlet-io/repo-wizard
Browse files Browse the repository at this point in the history
Github setup card; Repo wizard
  • Loading branch information
laszlocph authored May 6, 2024
2 parents 6260e0b + e9d7136 commit 8bcad4b
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 131 deletions.
4 changes: 4 additions & 0 deletions web/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
@apply bg-blue-500 dark:bg-blue-800 hover:bg-blue-400 dark:hover:bg-blue-900 focus:outline-none py-2 inline-flex items-center text-base font-normal font-sans rounded-md text-white dark:text-neutral-100 transition ease-in-out duration-150 text-nowrap
}

.secondaryButton {
@apply border-blue-500 dark:border-blue-700 text-blue-500 dark:text-blue-700 border hover:border-blue-400 dark:hover:border-blue-800 hover:text-blue-400 dark:hover:text-blue-800 inline-flex items-center px-6 py-2 text-base leading-6 font-medium rounded-md transition ease-in-out duration-150
}

.primaryButtonDisabled {
@apply bg-neutral-600 dark:bg-neutral-400 py-2 inline-flex items-center text-base font-normal font-sans rounded-md text-white dark:text-neutral-100 transition ease-in-out duration-150 text-nowrap cursor-default
}
Expand Down
2 changes: 1 addition & 1 deletion web/src/views/repo/previewView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function PreviewView(props) {
<h1 className="text-2xl leading-tight font-medium flex-grow">Previews</h1>
<button
type="button"
className='border-blue-500 dark:border-blue-700 text-blue-500 dark:text-blue-700 border hover:border-blue-400 dark:hover:border-blue-800 hover:text-blue-400 dark:hover:text-blue-800 cursor-pointer inline-flex items-center px-6 py-2 text-base leading-6 font-medium rounded-md transition ease-in-out duration-150'
className='secondaryButton'
onClick={() => props.history.push(encodeURI(`/repo/${owner}/${repo}/envs/${previewEnvConfig.env}/config/${repo}-preview/edit-preview`))}
>
Edit Preview Config
Expand Down
85 changes: 43 additions & 42 deletions web/src/views/repositories/repositories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
} from "../../redux/redux";
import { InformationCircleIcon } from '@heroicons/react/20/solid'
import FilterBar from '../filterBar/filterBar';
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/solid';
import { ImportWizard } from "../repositoryWizard/repositoryWizard";

export default function Repositories (props) {
const { store, gimletClient } = props;
Expand Down Expand Up @@ -68,12 +70,8 @@ export default function Repositories (props) {
return (
<div>
<header>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-12">
<div>
<div className="mt-8">
<SetupGithubCard history={props.history} />
</div>
</div>
<div className="max-w-7xl mx-auto pt-24 px-4 sm:px-6 lg:px-8">
<SetupGithubCard history={props.history} />
</div>
</header>
</div>
Expand Down Expand Up @@ -107,21 +105,25 @@ export default function Repositories (props) {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div>
<h4 className="text-lg font-base capitalize leading-tight mt-8 mb-5 pl-1">Repositories</h4>
<ul className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{ repositoriesLoading && <SkeletonLoader /> }
{ !repositoriesLoading && filteredRepositories.length === 0 && <EmptyStateNoMatchingService /> }
{ !repositoriesLoading && filteredRepositories.map(repo =>
<li key={repo} className="card">
<RepoCard
name={repo}
services={services[repo]}
navigateToRepo={() => props.history.push(`/repo/${repo}`)}
favorite={favorites.includes(repo)}
favoriteHandler={favoriteHandler}
/>
</li>
)}
</ul>
{ repositoriesLoading && <SkeletonLoader /> }
{ !repositoriesLoading && repositories.length === 0 ?
<ImportWizard {...props} />
:
<ul className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{ !repositoriesLoading && filteredRepositories.length === 0 && <EmptyStateNoMatchingService /> }
{ !repositoriesLoading && filteredRepositories.map(repo =>
<li key={repo} className="card">
<RepoCard
name={repo}
services={services[repo]}
navigateToRepo={() => props.history.push(`/repo/${repo}`)}
favorite={favorites.includes(repo)}
favoriteHandler={favoriteHandler}
/>
</li>
)}
</ul>
}
</div>
</div>
</main>
Expand Down Expand Up @@ -169,31 +171,30 @@ const filterRepos = (repos, services, favorites, filters) => {

const SetupGithubCard = (props) => {
return (
<div className="rounded-md bg-blue-50 p-4 mb-4">
<div className="flex">
<div className="flex-shrink-0">
<InformationCircleIcon className="h-5 w-5 text-blue-400" aria-hidden="true" />
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-blue-800">Integrate Github</h3>
<div className="mt-2 text-sm text-blue-700">
This view will load your git repositories once you integrated Github.<br />
<button
className="font-medium"
onClick={() => {props.history.push("/settings");return true}}
>
Click to integrate Github on the Settings page.
</button>
</div>
<ul className='w-full h-full card p-4'>
<div className="mx-auto text-center border-dashed border border-neutral-200 dark:border-neutral-700 rounded-md py-16">
<label htmlFor="label-title" className="font-medium">
Integrate Github
</label>
<p className="text-sm text-neutral-500 mt-4">
This view will load your git repositories once you integrated Github.
</p>
<button
className="externalLink"
onClick={() => { props.history.push("/settings"); return true } }
>
Click to integrate Github on the Settings page
</button>
<ArrowTopRightOnSquareIcon className="ml-1 externalLinkIcon" aria-hidden="true" />
<p className='learnMoreLink text-sm mt-6'><a href="https://gimlet.io">Learn more <ArrowTopRightOnSquareIcon className="externalLinkIcon" aria-hidden="true" /></a></p>
</div>
</div>
</div>
);
</ul>
)
}

export const SkeletonLoader = () => {
return (
<>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 list-none">
<li className="animate-pulse card">
<div className="w-full flex items-center justify-between p-6 space-x-6">
<div className="flex-1">
Expand All @@ -214,7 +215,7 @@ export const SkeletonLoader = () => {
</div>
</div>
</li>
</>
</div>
)
}

Expand Down
209 changes: 121 additions & 88 deletions web/src/views/repositoryWizard/repositoryWizard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,37 @@ import { ACTION_TYPE_GIT_REPOS } from '../../redux/redux';
import { Loading } from '../../components/deployStatus/deployStatus';

export default function RepositoryWizard(props) {
const { gimletClient, store } = props;
return (
<div className='text-neutral-900 dark:text-neutral-200'>
<div className="w-full bg-white dark:bg-neutral-800">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-32 flex items-center">
<div>
<h1 className="text-3xl leading-tight text-medium flex-grow">Import Git Repository</h1>
<div className='font-light text-sm pt-2 pb-16'>
To deploy an application, import its Git Repository first.
</div>
</div>
</div>
<div className="border-b border-neutral-200 dark:border-neutral-700"></div>
</div>
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 mt-8 flex">
<ImportWizard {...props} redirect />
</div>
</div>
)
}

export function ImportWizard(props) {
const { gimletClient, store, redirect } = props;
let reduxState = store.getState();

const [importedRepos, setImportedRepos] = useState(reduxState.gitRepos)
const [application, setApplication] = useState(reduxState.application)
const [user, setUser] = useState(reduxState.user)
const [repos, setRepos] = useState()
const [searchTerm, setSearchTerm] = useState("")
const [importing, setImporting] = useState(false)

store.subscribe(() => {
let reduxState = store.getState();
setImportedRepos(reduxState.gitRepos)
setApplication(reduxState.application)
setUser(reduxState.user)
});
Expand All @@ -35,6 +53,89 @@ export default function RepositoryWizard(props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchTerm])

return (
<div className="p-6 w-full h-[556px] card">
<div>
<label htmlFor="filter" className="sr-only">
Search
</label>
<div className="w-full">
<div className="relative">
<div className="absolute inset-y-0 left-0 flex items-center pl-3">
<svg data-testid="geist-icon" className="filterIcon" strokeLinejoin="round" viewBox="0 0 16 16">
<path fillRule="evenodd" clipRule="evenodd" d="M1.5 6.5C1.5 3.73858 3.73858 1.5 6.5 1.5C9.26142 1.5 11.5 3.73858 11.5 6.5C11.5 9.26142 9.26142 11.5 6.5 11.5C3.73858 11.5 1.5 9.26142 1.5 6.5ZM6.5 0C2.91015 0 0 2.91015 0 6.5C0 10.0899 2.91015 13 6.5 13C8.02469 13 9.42677 12.475 10.5353 11.596L13.9697 15.0303L14.5 15.5607L15.5607 14.5L15.0303 13.9697L11.596 10.5353C12.475 9.42677 13 8.02469 13 6.5C13 2.91015 10.0899 0 6.5 0Z" fill="currentColor"></path>
</svg>
</div>
<input
onChange={e => setSearchTerm(e.target.value)}
type="text"
name="filter"
id="filter"
className="filter"
placeholder="Search..."
/>
</div>
</div>
</div>
{repos ?
<>
<div className='relative w-full bg-white dark:bg-neutral-800 rounded-md ring-1 ring-inset ring-neutral-200 dark:ring-neutral-700 mt-10'>
{repos.length === 0 && <NoRepos searchTerm={searchTerm} user={user.login} installationUrl={application.installationURL} />}
<div className='divide-y dark:divide-neutral-700'>
{
repos.slice(0, 5).map((repo, idx) => {
return (
<li key={idx} className="flex items-center justify-between space-x-3 p-4">
<div className="flex min-w-0 flex-1 items-center space-x-3">
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-neutral-900 dark:text-neutral-300">{repo}</p>
</div>
</div>
<ImportButton {...props} repo={repo} />
</li>)
})
}
</div>
</div>
<div className="flex items-center justify-between mt-4">
{repos.length !== 0 &&
<div className='text-sm text-neutral-600 dark:text-neutral-400 py-4'>
Missing a Git repository? <a href={application.installationURL} rel="noreferrer" target="_blank" className='text-blue-500'>Adjust Github App Permission<ArrowRightIcon className="size-4 inline" aria-hidden="true" /></a>
</div>
}
{redirect &&
<button
type="button"
className='secondaryButton ml-auto'
onClick={() => props.history.push("/repositories")}
>
I am done importing
</button>
}
</div>
</>
:
<div className="flex w-full h-full items-center justify-center">
<label htmlFor="label-title" className="text-neutral-700 dark:text-neutral-300 text-sm">Searching...</label>
</div>
}
</div>
)
}

function ImportButton(props) {
const { repo } = props;
const { gimletClient, store } = props;
let reduxState = store.getState();

const [importedRepos, setImportedRepos] = useState(reduxState.gitRepos)
const [importing, setImporting] = useState(false)

store.subscribe(() => {
let reduxState = store.getState();
setImportedRepos(reduxState.gitRepos)
});

const importRepo = (name) => {
setImporting(true)
gimletClient.importRepo(name)
Expand All @@ -49,91 +150,23 @@ export default function RepositoryWizard(props) {
});
}

const imported = importedRepos.includes(repo);

return (
<div className='text-neutral-900 dark:text-neutral-200'>
<div className="w-full bg-white dark:bg-neutral-800">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-32 flex items-center">
<div>
<h1 className="text-3xl leading-tight text-medium flex-grow">Import Git Repository</h1>
<div className='font-light text-sm pt-2 pb-16'>
To deploy an application, import its Git Repository first.
</div>
</div>
</div>
<div className="border-b border-neutral-200 dark:border-neutral-700"></div>
</div>
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 mt-8 flex">
<div className="p-6 w-full h-[540px] card">
<div>
<label htmlFor="filter" className="sr-only">
Search
</label>
<div className="w-full">
<div className="relative">
<div className="absolute inset-y-0 left-0 flex items-center pl-3">
<svg data-testid="geist-icon" className="filterIcon" strokeLinejoin="round" viewBox="0 0 16 16">
<path fillRule="evenodd" clipRule="evenodd" d="M1.5 6.5C1.5 3.73858 3.73858 1.5 6.5 1.5C9.26142 1.5 11.5 3.73858 11.5 6.5C11.5 9.26142 9.26142 11.5 6.5 11.5C3.73858 11.5 1.5 9.26142 1.5 6.5ZM6.5 0C2.91015 0 0 2.91015 0 6.5C0 10.0899 2.91015 13 6.5 13C8.02469 13 9.42677 12.475 10.5353 11.596L13.9697 15.0303L14.5 15.5607L15.5607 14.5L15.0303 13.9697L11.596 10.5353C12.475 9.42677 13 8.02469 13 6.5C13 2.91015 10.0899 0 6.5 0Z" fill="currentColor"></path>
</svg>
</div>
<input
onChange={e => setSearchTerm(e.target.value)}
type="text"
name="filter"
id="filter"
className="filter"
placeholder="Search..."
/>
</div>
</div>
</div>
{repos ?
<>
<div className='w-full bg-white dark:bg-neutral-800 rounded-md ring-1 ring-inset ring-neutral-200 dark:ring-neutral-700 mt-10'>
{repos.length === 0 && <NoRepos searchTerm={searchTerm} user={user.login} installationUrl={application.installationURL} />}
<div className='divide-y dark:divide-neutral-700'>
{
repos.slice(0, 5).map((repo, idx) => {
const imported = importedRepos.includes(repo);
return (
<li key={idx} className="flex items-center justify-between space-x-3 p-4">
<div className="flex min-w-0 flex-1 items-center space-x-3">
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-neutral-900 dark:text-neutral-300">{repo}</p>
</div>
</div>
<div className="flex-shrink-0">
{imported ?
<button className="cursor-default bg-neutral-200 dark:bg-neutral-700 inline-flex items-center px-3 py-2 border border-transparent font-normal text-base font-sans rounded-md text-neutral-400" >
Imported
</button>
:
<button
disabled={importing}
onClick={() => importRepo(repo)}
className={`${importing ? 'primaryButtonDisabled': 'primaryButton'} px-3`}
>
{importing ? <><Loading />Importing</> : 'Import'}
</button>
}
</div>
</li>)
})
}
</div>
</div>
{repos.length !== 0 &&
<div className='text-sm text-neutral-600 dark:text-neutral-400 py-4'>
Missing a Git repository? <a href={application.installationURL} rel="noreferrer" target="_blank" className='text-blue-500'>Adjust Github App Permission<ArrowRightIcon className="size-4 inline" aria-hidden="true" /></a>
</div>
}
</>
:
<div className="flex w-full h-full items-center justify-center">
<label htmlFor="label-title" className="text-neutral-700 dark:text-neutral-300 text-sm">Searching...</label>
</div>
}
</div>
</div>
<div className="flex-shrink-0">
{imported ?
<button className="cursor-default bg-neutral-200 dark:bg-neutral-700 inline-flex items-center px-3 py-2 border border-transparent font-normal text-base font-sans rounded-md text-neutral-400" >
Imported
</button>
:
<button
disabled={importing}
onClick={() => importRepo(repo)}
className={`${importing ? 'primaryButtonDisabled' : 'primaryButton'} px-3`}
>
{importing ? <><Loading />Importing</> : 'Import'}
</button>
}
</div>
)
}
Expand Down

0 comments on commit 8bcad4b

Please sign in to comment.