From 99eb514306e38239cbc2b222cdd3b647bc332b78 Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Wed, 28 Feb 2024 21:16:26 +0700 Subject: [PATCH 01/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20restruct?= =?UTF-8?q?ured=20and=20restryling=20repository=20list=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => RepositoryList}/RepositoryList.tsx | 92 +++++++++++-------- .../projects/create/RepositoryList/index.ts | 1 + .../pages/org-slug/projects/create/index.tsx | 8 +- 3 files changed, 61 insertions(+), 40 deletions(-) rename packages/frontend/src/components/projects/create/{ => RepositoryList}/RepositoryList.tsx (59%) create mode 100644 packages/frontend/src/components/projects/create/RepositoryList/index.ts diff --git a/packages/frontend/src/components/projects/create/RepositoryList.tsx b/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx similarity index 59% rename from packages/frontend/src/components/projects/create/RepositoryList.tsx rename to packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx index f09e61d..171082e 100644 --- a/packages/frontend/src/components/projects/create/RepositoryList.tsx +++ b/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx @@ -3,13 +3,17 @@ import { Octokit } from 'octokit'; import assert from 'assert'; 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 './ProjectRepoCard'; -import { GitOrgDetails, GitRepositoryDetails } from '../../../types'; -import AsyncSelect from '../../shared/AsyncSelect'; -import { GithubIcon } from 'components/shared/CustomIcon'; +import { ProjectRepoCard } from 'components/projects/create/ProjectRepoCard'; +import { GitOrgDetails, GitRepositoryDetails } from 'types'; +import { + ChevronGrabberHorizontal, + GithubIcon, + SearchIcon, +} from 'components/shared/CustomIcon'; +import { Select, SelectOption } from 'components/shared/Select'; +import { Input } from 'components/shared/Input'; const DEFAULT_SEARCHED_REPO = ''; const REPOS_PER_PAGE = 5; @@ -18,9 +22,9 @@ interface RepositoryListProps { octokit: Octokit; } -const RepositoryList = ({ octokit }: RepositoryListProps) => { +export const RepositoryList = ({ octokit }: RepositoryListProps) => { const [searchedRepo, setSearchedRepo] = useState(DEFAULT_SEARCHED_REPO); - const [selectedAccount, setSelectedAccount] = useState(''); + const [selectedAccount, setSelectedAccount] = useState(); const [orgs, setOrgs] = useState([]); // TODO: Add new type for Git user when required const [gitUser, setGitUser] = useState(); @@ -35,7 +39,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => { const orgs = await octokit.rest.orgs.listForAuthenticatedUser(); setOrgs(orgs.data); setGitUser(user.data); - setSelectedAccount(user.data.login); + setSelectedAccount({ label: user.data.login, value: user.data.login }); }; fetchUserAndOrgs(); @@ -54,7 +58,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => { let query = `${debouncedSearchedRepo} in:name fork:true`; // Check if selected account is an organization - if (selectedAccount === gitUser.login) { + if (selectedAccount.value === gitUser.login) { query = query + ` user:${selectedAccount}`; } else { query = query + ` org:${selectedAccount}`; @@ -69,7 +73,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => { return; } - if (selectedAccount === gitUser.login) { + if (selectedAccount.value === gitUser.login) { const result = await octokit.rest.repos.listForAuthenticatedUser({ per_page: REPOS_PER_PAGE, affiliation: 'owner', @@ -78,7 +82,9 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => { 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'); const result = await octokit.rest.repos.listForOrg({ @@ -96,7 +102,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => { const handleResetFilters = useCallback(() => { assert(gitUser, 'Git user is not available'); setSearchedRepo(DEFAULT_SEARCHED_REPO); - setSelectedAccount(gitUser.login); + setSelectedAccount({ label: gitUser.login, value: gitUser.login }); }, [gitUser]); const accounts = useMemo(() => { @@ -107,35 +113,51 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => { return [gitUser, ...orgs]; }, [octokit, orgs, gitUser]); + const options = useMemo(() => { + return accounts.map((account) => ({ + label: account.login, + value: account.login, + leftIcon: , + })); + }, [accounts]); + return ( -
-
-
- + {/* Dropdown and search */} +
+
+ setSearchedRepo(event.target.value)} placeholder="Search for repository" + leftIcon={} + onChange={(e) => setSearchedRepo(e.target.value)} />
+ + {/* Repository list */} {Boolean(repositoryDetails.length) ? ( - repositoryDetails.map((repo, key) => { - return ; - }) +
+ {repositoryDetails.map((repo, index) => ( + <> + + {/* Horizontal line */} + {index !== repositoryDetails.length - 1 && ( +
+ )} + + ))} +
) : (
@@ -151,8 +173,6 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
)} -
+ ); }; - -export default RepositoryList; diff --git a/packages/frontend/src/components/projects/create/RepositoryList/index.ts b/packages/frontend/src/components/projects/create/RepositoryList/index.ts new file mode 100644 index 0000000..dc3bc8c --- /dev/null +++ b/packages/frontend/src/components/projects/create/RepositoryList/index.ts @@ -0,0 +1 @@ +export * from './RepositoryList'; diff --git a/packages/frontend/src/pages/org-slug/projects/create/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/index.tsx index 1c86ee3..49eeed9 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/index.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; 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 { useOctokit } from 'context/OctokitContext'; import { Heading } from 'components/shared/Heading'; @@ -13,8 +13,8 @@ const NewProject = () => { return isAuth ? ( <>
- - Start with template + + Start with a template
{templates.map((template) => { @@ -28,7 +28,7 @@ const NewProject = () => { })}
- + Import a repository From eda2cc76edad4b94d83f26c0e38fe1dbc73cba57 Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Wed, 28 Feb 2024 21:16:59 +0700 Subject: [PATCH 02/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20restruct?= =?UTF-8?q?ured=20and=20restyling=20project=20repo=20card=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projects/create/ProjectRepoCard.tsx | 81 ---------------- .../ProjectRepoCard/ProjectRepoCard.tsx | 94 +++++++++++++++++++ .../projects/create/ProjectRepoCard/index.ts | 1 + 3 files changed, 95 insertions(+), 81 deletions(-) delete mode 100644 packages/frontend/src/components/projects/create/ProjectRepoCard.tsx create mode 100644 packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx create mode 100644 packages/frontend/src/components/projects/create/ProjectRepoCard/index.ts diff --git a/packages/frontend/src/components/projects/create/ProjectRepoCard.tsx b/packages/frontend/src/components/projects/create/ProjectRepoCard.tsx deleted file mode 100644 index fbcf50b..0000000 --- a/packages/frontend/src/components/projects/create/ProjectRepoCard.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React, { useCallback } from 'react'; -import toast from 'react-hot-toast'; -import { useNavigate, useParams } from 'react-router-dom'; - -import { Chip, IconButton, Spinner } from '@material-tailwind/react'; - -import { relativeTimeISO } from '../../../utils/time'; -import { GitRepositoryDetails } from '../../../types'; -import { useGQLClient } from '../../../context/GQLClientContext'; -import { GithubIcon, LockIcon } from 'components/shared/CustomIcon'; - -interface ProjectRepoCardProps { - repository: GitRepositoryDetails; -} - -const ProjectRepoCard: React.FC = ({ repository }) => { - const client = useGQLClient(); - const navigate = useNavigate(); - const [isLoading, setIsLoading] = React.useState(false); - - const { orgSlug } = useParams(); - - const createProject = useCallback(async () => { - if (!repository) { - return; - } - - setIsLoading(true); - const { addProject } = await client.addProject(orgSlug!, { - name: `${repository.owner!.login}-${repository.name}`, - prodBranch: repository.default_branch!, - repository: repository.full_name, - // TODO: Compute template from repo - template: 'webapp', - }); - - if (Boolean(addProject)) { - setIsLoading(false); - navigate(`import?projectId=${addProject.id}`); - } else { - setIsLoading(false); - toast.error('Failed to create project'); - } - }, [client, repository]); - - return ( -
-
- -
-
-
- {repository.full_name} - {repository.visibility === 'private' && ( - } - /> - )} -
-

{repository.updated_at && relativeTimeISO(repository.updated_at)}

-
- {isLoading ? ( - - ) : ( -
- - {'>'} - -
- )} -
- ); -}; - -export default ProjectRepoCard; diff --git a/packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx b/packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx new file mode 100644 index 0000000..6c8363e --- /dev/null +++ b/packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx @@ -0,0 +1,94 @@ +import React, { useCallback } from 'react'; +import toast from 'react-hot-toast'; +import { useNavigate, useParams } from 'react-router-dom'; + +import { Spinner } from '@material-tailwind/react'; + +import { relativeTimeISO } from 'utils/time'; +import { GitRepositoryDetails } from 'types'; +import { useGQLClient } from 'context/GQLClientContext'; +import { + ArrowRightCircleIcon, + GithubIcon, + LockIcon, +} from 'components/shared/CustomIcon'; +import { Button } from 'components/shared/Button'; + +interface ProjectRepoCardProps { + repository: GitRepositoryDetails; +} + +export const ProjectRepoCard: React.FC = ({ + repository, +}) => { + const client = useGQLClient(); + const navigate = useNavigate(); + const [isLoading, setIsLoading] = React.useState(false); + + const { orgSlug } = useParams(); + + const createProject = useCallback(async () => { + if (!repository) { + return; + } + + setIsLoading(true); + const { addProject } = await client.addProject(orgSlug!, { + name: `${repository.owner!.login}-${repository.name}`, + prodBranch: repository.default_branch!, + repository: repository.full_name, + // TODO: Compute template from repo + template: 'webapp', + }); + + if (Boolean(addProject)) { + setIsLoading(false); + navigate(`import?projectId=${addProject.id}`); + } else { + setIsLoading(false); + toast.error('Failed to create project'); + } + }, [client, repository]); + + return ( +
+ {/* Icon container */} +
+ +
+ {/* Content */} +
+
+

+ {repository.full_name} +

+

+ {repository.updated_at && relativeTimeISO(repository.updated_at)} +

+
+ {repository.visibility === 'private' && ( +
+ + Private +
+ )} +
+ {/* Right action */} + {isLoading ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/packages/frontend/src/components/projects/create/ProjectRepoCard/index.ts b/packages/frontend/src/components/projects/create/ProjectRepoCard/index.ts new file mode 100644 index 0000000..7847249 --- /dev/null +++ b/packages/frontend/src/components/projects/create/ProjectRepoCard/index.ts @@ -0,0 +1 @@ +export * from './ProjectRepoCard'; From 769593913e22b2ea6ac71d50a67f6f6bdf6ef60f Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Wed, 28 Feb 2024 21:17:30 +0700 Subject: [PATCH 03/37] =?UTF-8?q?=F0=9F=90=9B=20fix:=20button=20inside=20b?= =?UTF-8?q?utton=20console=20browser=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projects/create/TemplateCard/TemplateCard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx index b7c549f..b26ff85 100644 --- a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx +++ b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx @@ -49,9 +49,9 @@ export const TemplateCard = ({ template, isGitAuth }: TemplateCardProps) => { }, [isGitAuth, navigate, template, toast]); return ( - )} - +
); }; From eb4ccfbc9cb4a7cef1c5522b28d3e8ba1ac834e7 Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Wed, 28 Feb 2024 21:17:59 +0700 Subject: [PATCH 04/37] =?UTF-8?q?=F0=9F=8E=A8=20style:=20make=20the=20inpu?= =?UTF-8?q?t=20width=20full?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/shared/Input/Input.theme.ts | 4 +++- packages/frontend/src/components/shared/Input/Input.tsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/shared/Input/Input.theme.ts b/packages/frontend/src/components/shared/Input/Input.theme.ts index b1ecb70..0144d57 100644 --- a/packages/frontend/src/components/shared/Input/Input.theme.ts +++ b/packages/frontend/src/components/shared/Input/Input.theme.ts @@ -8,6 +8,8 @@ export const inputTheme = tv( 'items-center', 'rounded-lg', 'relative', + 'gap-2', + 'w-full', 'placeholder:text-elements-disabled', 'disabled:cursor-not-allowed', 'disabled:bg-controls-disabled', @@ -27,7 +29,7 @@ export const inputTheme = tv( 'disabled:shadow-none', 'disabled:border-none', ], - icon: ['text-elements-mid-em'], + icon: ['text-elements-low-em'], iconContainer: [ 'absolute', 'inset-y-0', diff --git a/packages/frontend/src/components/shared/Input/Input.tsx b/packages/frontend/src/components/shared/Input/Input.tsx index fb3fd7d..dd88bd8 100644 --- a/packages/frontend/src/components/shared/Input/Input.tsx +++ b/packages/frontend/src/components/shared/Input/Input.tsx @@ -87,12 +87,12 @@ export const Input = ({ ); return ( -
+
{renderLabels}
{leftIcon && renderLeftIcon} Date: Wed, 28 Feb 2024 21:18:29 +0700 Subject: [PATCH 05/37] =?UTF-8?q?=F0=9F=90=9B=20fix:=20controlled=20and=20?= =?UTF-8?q?uncontroller=20console=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/shared/Select/Select.theme.ts | 4 +-- .../src/components/shared/Select/Select.tsx | 34 +++++-------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/packages/frontend/src/components/shared/Select/Select.theme.ts b/packages/frontend/src/components/shared/Select/Select.theme.ts index 43d4b0f..fed6531 100644 --- a/packages/frontend/src/components/shared/Select/Select.theme.ts +++ b/packages/frontend/src/components/shared/Select/Select.theme.ts @@ -85,7 +85,7 @@ export const selectTheme = tv({ size: { md: { container: ['min-h-11'], - inputWrapper: ['min-h-11', 'text-sm', 'pl-4', 'pr-4', 'py-1'], + inputWrapper: ['min-h-11', 'text-sm', 'pl-4', 'pr-4'], icon: ['h-[18px]', 'w-[18px]'], helperText: 'text-sm', helperIcon: ['h-5', 'w-5'], @@ -93,7 +93,7 @@ export const selectTheme = tv({ }, sm: { container: ['min-h-8'], - inputWrapper: ['min-h-8', 'text-xs', 'pl-3', 'pr-3', 'py-0.5'], + inputWrapper: ['min-h-8', 'text-xs', 'pl-3', 'pr-3'], icon: ['h-4', 'w-4'], helperText: 'text-xs', helperIcon: ['h-4', 'w-4'], diff --git a/packages/frontend/src/components/shared/Select/Select.tsx b/packages/frontend/src/components/shared/Select/Select.tsx index 963cc0b..8c7e914 100644 --- a/packages/frontend/src/components/shared/Select/Select.tsx +++ b/packages/frontend/src/components/shared/Select/Select.tsx @@ -3,7 +3,6 @@ import React, { useState, ComponentPropsWithoutRef, useMemo, - useCallback, MouseEvent, useRef, useEffect, @@ -135,7 +134,9 @@ export const Select = ({ const theme = selectTheme({ size, error, variant, orientation }); const [inputValue, setInputValue] = useState(''); - const [selectedItem, setSelectedItem] = useState(null); + const [selectedItem, setSelectedItem] = useState( + (value as SelectOption) || null, + ); const [dropdownOpen, setDropdownOpen] = useState(false); const [dropdownPosition, setDropdownPosition] = useState<'top' | 'bottom'>( 'bottom', @@ -166,22 +167,6 @@ export const Select = ({ } }, [dropdownOpen]); // Re-calculate whenever the dropdown is opened - useEffect(() => { - // If multiple selection is enabled, ensure the internal state is an array - if (multiple) { - if (Array.isArray(value)) { - // Directly use the provided array - setSelectedItems(value); - } else { - // Reset or set to empty array if the value is not an array - setSelectedItems([]); - } - } else { - // For single selection, directly set the selected item - setSelectedItem(value as SelectOption); - } - }, [value, multiple]); - const handleSelectedItemChange = (selectedItem: SelectOption | null) => { setSelectedItem(selectedItem); setInputValue(selectedItem ? selectedItem.label : ''); @@ -194,9 +179,10 @@ export const Select = ({ addSelectedItem, removeSelectedItem, selectedItems, - setSelectedItems, + // setSelectedItems, reset, } = useMultipleSelection({ + selectedItems: multiple ? (value as SelectOption[]) : [], onSelectedItemsChange: multiple ? undefined : ({ selectedItems }) => { @@ -234,6 +220,7 @@ export const Select = ({ openMenu, } = useCombobox({ items: filteredItems, + selectedItem: multiple ? null : (value as SelectOption) || null, // @ts-expect-error – there are two params but we don't need the second one isItemDisabled: (item) => item.disabled, onInputValueChange: ({ inputValue = '' }) => setInputValue(inputValue), @@ -265,16 +252,12 @@ export const Select = ({ setInputValue(''); } }, - selectedItem: multiple ? null : selectedItem, // TODO: Make the input value empty when the dropdown is open, has a value, it is not multiple, and searchable itemToString: (item) => (item && !multiple ? item.label : ''), }); - const isSelected = useCallback( - (item: SelectOption) => - multiple ? selectedItems.includes(item) : selectedItem === item, - [selectedItems, selectedItem, multiple], - ); + const isSelected = (item: SelectOption) => + multiple ? selectedItems.includes(item) : selectedItem === item; const handleClear = (e: MouseEvent) => { e.stopPropagation(); @@ -336,6 +319,7 @@ export const Select = ({ const isMultipleHasValue = multiple && selectedItems.length > 0; const isMultipleHasValueButNotSearchable = multiple && !searchable && selectedItems.length > 0; + const displayPlaceholder = useMemo(() => { if (hideValues && isMultipleHasValue) { return `${selectedItems.length} selected`; From 075cec58e5d039ef4c3dd5c7feb6349df43dd715 Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Wed, 28 Feb 2024 21:27:04 +0700 Subject: [PATCH 06/37] =?UTF-8?q?=F0=9F=8E=A8=20style:=20add=20github=20ic?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projects/create/RepositoryList/RepositoryList.tsx | 1 + packages/frontend/src/components/shared/Select/Select.tsx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx b/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx index 171082e..14af57e 100644 --- a/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx +++ b/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx @@ -130,6 +130,7 @@ export const RepositoryList = ({ octokit }: RepositoryListProps) => { options={options} placeholder="Select a repository" value={selectedAccount} + leftIcon={selectedAccount ? : undefined} rightIcon={} onChange={(value) => setSelectedAccount(value as SelectOption)} /> diff --git a/packages/frontend/src/components/shared/Select/Select.tsx b/packages/frontend/src/components/shared/Select/Select.tsx index 8c7e914..0e82edc 100644 --- a/packages/frontend/src/components/shared/Select/Select.tsx +++ b/packages/frontend/src/components/shared/Select/Select.tsx @@ -375,6 +375,8 @@ export const Select = ({ 'w-6': isMultipleHasValueButNotSearchable && !hideValues, // Add margin to the X icon 'ml-6': isMultipleHasValueButNotSearchable && clearable, + // Add padding if there's a left icon + 'pl-7': leftIcon, }, )} /> From f08081932c9b298a96fff00057b27fba4539e2bd Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Thu, 29 Feb 2024 09:21:28 +0700 Subject: [PATCH 07/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20use=20cu?= =?UTF-8?q?stom=20toast=20component=20and=20handle=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectRepoCard/ProjectRepoCard.tsx | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx b/packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx index 6c8363e..b06fc23 100644 --- a/packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx +++ b/packages/frontend/src/components/projects/create/ProjectRepoCard/ProjectRepoCard.tsx @@ -1,5 +1,4 @@ -import React, { useCallback } from 'react'; -import toast from 'react-hot-toast'; +import React, { useCallback, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { Spinner } from '@material-tailwind/react'; @@ -13,6 +12,7 @@ import { LockIcon, } from 'components/shared/CustomIcon'; import { Button } from 'components/shared/Button'; +import { useToast } from 'components/shared/Toast'; interface ProjectRepoCardProps { repository: GitRepositoryDetails; @@ -23,32 +23,52 @@ export const ProjectRepoCard: React.FC = ({ }) => { const client = useGQLClient(); const navigate = useNavigate(); - const [isLoading, setIsLoading] = React.useState(false); + const [isLoading, setIsLoading] = useState(false); const { orgSlug } = useParams(); + const { toast, dismiss } = useToast(); const createProject = useCallback(async () => { - if (!repository) { - return; + if (!repository || !orgSlug) { + return toast({ + id: 'missing-repository-or-org-slug', + title: 'Repository or organization slug is missing', + variant: 'error', + onDismiss: dismiss, + }); } - setIsLoading(true); - const { addProject } = await client.addProject(orgSlug!, { - name: `${repository.owner!.login}-${repository.name}`, - prodBranch: repository.default_branch!, - repository: repository.full_name, - // TODO: Compute template from repo - template: 'webapp', - }); - - if (Boolean(addProject)) { + try { + setIsLoading(true); + const { addProject } = await client.addProject(orgSlug, { + name: `${repository.owner?.login}-${repository.name}`, + prodBranch: repository.default_branch as string, + repository: repository.full_name, + // TODO: Compute template from repo + template: 'webapp', + }); + if (addProject) { + navigate(`import?projectId=${addProject.id}`); + } else { + toast({ + id: 'failed-to-create-project', + title: 'Failed to create project', + variant: 'error', + onDismiss: dismiss, + }); + } + } catch (error) { + console.error(error); + toast({ + id: 'failed-to-create-project', + title: 'Failed to create project', + variant: 'error', + onDismiss: dismiss, + }); + } finally { setIsLoading(false); - navigate(`import?projectId=${addProject.id}`); - } else { - setIsLoading(false); - toast.error('Failed to create project'); } - }, [client, repository]); + }, [client, repository, orgSlug, setIsLoading, navigate, toast]); return (
Date: Thu, 29 Feb 2024 09:21:49 +0700 Subject: [PATCH 08/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20remove?= =?UTF-8?q?=20unused=20comment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/shared/Select/Select.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/frontend/src/components/shared/Select/Select.tsx b/packages/frontend/src/components/shared/Select/Select.tsx index 0e82edc..fa22428 100644 --- a/packages/frontend/src/components/shared/Select/Select.tsx +++ b/packages/frontend/src/components/shared/Select/Select.tsx @@ -179,7 +179,6 @@ export const Select = ({ addSelectedItem, removeSelectedItem, selectedItems, - // setSelectedItems, reset, } = useMultipleSelection({ selectedItems: multiple ? (value as SelectOption[]) : [], From 2cd1865d1986f100b56b153d6ada7584ab280212 Mon Sep 17 00:00:00 2001 From: Andre H Date: Thu, 29 Feb 2024 15:30:34 +0700 Subject: [PATCH 09/37] =?UTF-8?q?=F0=9F=94=A7=20chore:=20add=20linkchain?= =?UTF-8?q?=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shared/CustomIcon/LinkChainIcon.tsx | 21 +++++++++++++++++++ .../src/components/shared/CustomIcon/index.ts | 1 + 2 files changed, 22 insertions(+) create mode 100644 packages/frontend/src/components/shared/CustomIcon/LinkChainIcon.tsx diff --git a/packages/frontend/src/components/shared/CustomIcon/LinkChainIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/LinkChainIcon.tsx new file mode 100644 index 0000000..66ff9cd --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/LinkChainIcon.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const LinkChainIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts index b996518..2d610c4 100644 --- a/packages/frontend/src/components/shared/CustomIcon/index.ts +++ b/packages/frontend/src/components/shared/CustomIcon/index.ts @@ -40,6 +40,7 @@ export * from './GithubStrokeIcon'; export * from './BranchStrokeIcon'; export * from './StorageIcon'; export * from './LinkIcon'; +export * from './LinkChainIcon'; export * from './CursorBoxIcon'; // Templates From d9392c095d748237638caca3f779a272c66d74af Mon Sep 17 00:00:00 2001 From: Andre H Date: Thu, 29 Feb 2024 15:31:10 +0700 Subject: [PATCH 10/37] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20reskin=20tem?= =?UTF-8?q?plate=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org-slug/projects/create/Template.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/pages/org-slug/projects/create/Template.tsx b/packages/frontend/src/pages/org-slug/projects/create/Template.tsx index d819a6f..b764e45 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/Template.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/Template.tsx @@ -10,6 +10,8 @@ import { Avatar } from '@material-tailwind/react'; import Stepper from '../../../../components/Stepper'; import templates from '../../../../assets/templates'; +import { LinkChainIcon } from 'components/shared/CustomIcon'; +import { Heading } from 'components/shared/Heading'; // TODO: Set dynamic route for template and load details from DB const CreateWithTemplate = () => { @@ -44,19 +46,24 @@ const CreateWithTemplate = () => { return (
-
- -
{template?.name}
+ From a8ad6c6eec9be4b00ac00afc47a9ce2cf1ca4120 Mon Sep 17 00:00:00 2001 From: Andre H Date: Fri, 1 Mar 2024 10:41:12 +0800 Subject: [PATCH 11/37] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20implement=20?= =?UTF-8?q?card=20style=20for=20radio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/shared/Radio/Radio.theme.ts | 21 ++++++++++++- .../src/components/shared/Radio/Radio.tsx | 5 +-- .../src/components/shared/Radio/RadioItem.tsx | 31 ++++++++++++++----- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/packages/frontend/src/components/shared/Radio/Radio.theme.ts b/packages/frontend/src/components/shared/Radio/Radio.theme.ts index 0b84601..84d8fd0 100644 --- a/packages/frontend/src/components/shared/Radio/Radio.theme.ts +++ b/packages/frontend/src/components/shared/Radio/Radio.theme.ts @@ -2,7 +2,7 @@ import { VariantProps, tv } from 'tailwind-variants'; export const radioTheme = tv({ slots: { - root: ['flex', 'gap-3', 'flex-wrap'], + root: ['flex', 'gap-3'], wrapper: ['flex', 'items-center', 'gap-2', 'group'], label: ['text-sm', 'tracking-[-0.006em]', 'text-elements-high-em'], radio: [ @@ -39,15 +39,34 @@ export const radioTheme = tv({ 'after:data-[state=checked]:group-hover:bg-elements-on-primary', 'after:data-[state=checked]:group-focus-visible:bg-elements-on-primary', ], + icon: ['w-[18px]', 'h-[18px]'], }, variants: { orientation: { vertical: { root: ['flex-col'] }, horizontal: { root: ['flex-row'] }, }, + variant: { + unstyled: {}, + card: { + wrapper: [ + 'px-4', + 'py-3', + 'rounded-lg', + 'border', + 'border-border-interactive', + 'bg-controls-tertiary', + 'shadow-button', + 'w-full', + 'cursor-pointer', + ], + label: ['select-none', 'cursor-pointer'], + }, + }, }, defaultVariants: { orientation: 'vertical', + variant: 'unstyled', }, }); diff --git a/packages/frontend/src/components/shared/Radio/Radio.tsx b/packages/frontend/src/components/shared/Radio/Radio.tsx index 9654249..8046800 100644 --- a/packages/frontend/src/components/shared/Radio/Radio.tsx +++ b/packages/frontend/src/components/shared/Radio/Radio.tsx @@ -49,14 +49,15 @@ export const Radio = ({ className, options, orientation, + variant, ...props }: RadioProps) => { - const { root } = radioTheme({ orientation }); + const { root } = radioTheme({ orientation, variant }); return ( {options.map((option) => ( - + ))} ); diff --git a/packages/frontend/src/components/shared/Radio/RadioItem.tsx b/packages/frontend/src/components/shared/Radio/RadioItem.tsx index 177af9d..4a2c2e3 100644 --- a/packages/frontend/src/components/shared/Radio/RadioItem.tsx +++ b/packages/frontend/src/components/shared/Radio/RadioItem.tsx @@ -1,13 +1,16 @@ -import React, { ComponentPropsWithoutRef } from 'react'; +import React, { ReactNode, ComponentPropsWithoutRef } from 'react'; import { Item as RadixRadio, Indicator as RadixIndicator, RadioGroupItemProps, RadioGroupIndicatorProps, } from '@radix-ui/react-radio-group'; -import { radioTheme } from './Radio.theme'; +import { RadioTheme, radioTheme } from './Radio.theme'; +import { cloneIcon } from 'utils/cloneIcon'; -export interface RadioItemProps extends RadioGroupItemProps { +export interface RadioItemProps + extends RadioGroupItemProps, + Pick { /** * The wrapper props of the radio item. * You can use this prop to customize the wrapper props. @@ -27,6 +30,10 @@ export interface RadioItemProps extends RadioGroupItemProps { * The id of the radio item. */ id?: string; + /** + * The left icon of the radio item. + */ + leftIcon?: ReactNode; /** * The label of the radio item. */ @@ -41,18 +48,26 @@ export const RadioItem = ({ wrapperProps, labelProps, indicatorProps, + leftIcon, label, id, + variant, ...props }: RadioItemProps) => { - const { wrapper, label: labelClass, radio, indicator } = radioTheme(); + const { + wrapper, + label: labelClass, + radio, + indicator, + icon, + } = radioTheme({ variant }); // Generate a unique id for the radio item from the label if the id is not provided const kebabCaseLabel = label?.toLowerCase().replace(/\s+/g, '-'); const componentId = id ?? kebabCaseLabel; return ( -
+
+ ); }; From 0b64ccc36405e8f4be2e37d637758a20a616c90c Mon Sep 17 00:00:00 2001 From: Andre H Date: Fri, 1 Mar 2024 11:12:47 +0800 Subject: [PATCH 12/37] =?UTF-8?q?=F0=9F=94=A7=20chore:=20replace=20space-y?= =?UTF-8?q?-2=20with=20gap-y-2=20for=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/shared/Input/Input.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/shared/Input/Input.tsx b/packages/frontend/src/components/shared/Input/Input.tsx index fb3fd7d..5bd5265 100644 --- a/packages/frontend/src/components/shared/Input/Input.tsx +++ b/packages/frontend/src/components/shared/Input/Input.tsx @@ -87,12 +87,12 @@ export const Input = ({ ); return ( -
+
{renderLabels}
{leftIcon && renderLeftIcon} Date: Fri, 1 Mar 2024 11:13:17 +0800 Subject: [PATCH 13/37] =?UTF-8?q?=F0=9F=94=A7=20chore:=20reskin=20with=20s?= =?UTF-8?q?hared=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projects/create/template/index.tsx | 144 ++++++++---------- 1 file changed, 64 insertions(+), 80 deletions(-) diff --git a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx index 9ff50c8..8f8a0f3 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx @@ -1,15 +1,23 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { useForm, Controller, SubmitHandler } from 'react-hook-form'; +import { useForm, SubmitHandler } from 'react-hook-form'; import { useNavigate, useOutletContext, useParams } from 'react-router-dom'; import toast from 'react-hot-toast'; import assert from 'assert'; -import { Button, Option, Typography } from '@material-tailwind/react'; - import { useOctokit } from '../../../../../context/OctokitContext'; import { useGQLClient } from '../../../../../context/GQLClientContext'; -import AsyncSelect from '../../../../../components/shared/AsyncSelect'; import { Template } from '../../../../../types'; +import { Heading } from 'components/shared/Heading'; +import { Input } from 'components/shared/Input'; +import { Radio } from 'components/shared/Radio'; +import { Select } from 'components/shared/Select'; +import { + ArrowRightCircleFilledIcon, + GitIcon, + GitTeaIcon, +} from 'components/shared/CustomIcon'; +import { Checkbox } from 'components/shared/Checkbox'; +import { Button } from 'components/shared/Button'; type SubmitRepoValues = { framework: string; @@ -30,6 +38,19 @@ const CreateRepo = () => { const [gitAccounts, setGitAccounts] = useState([]); const [isLoading, setIsLoading] = useState(false); + const FRAMEWORK_OPTIONS = [ + { + value: 'react-native', + label: 'React Native', + leftIcon: , + }, + { + value: 'expo', + label: 'Expo', + leftIcon: , + }, + ]; + const submitRepoHandler: SubmitHandler = useCallback( async (data) => { assert(data.account); @@ -93,7 +114,7 @@ const CreateRepo = () => { fetchUserAndOrgs(); }, [octokit]); - const { register, handleSubmit, control, reset } = useForm({ + const { handleSubmit, reset } = useForm({ defaultValues: { framework: 'React', repoName: '', @@ -110,86 +131,49 @@ const CreateRepo = () => { return (
-
- - Create a repository - - - The project will be cloned into this repository - -
-
-
Framework
-
- - -
-
-
-
Git account
+
- ( - - {gitAccounts.map((account, key) => ( - - ))} - - )} + Create a repository + + The project will be cloned into this repository + +
+
+ Framework +
-
-
-
Name the repo
+
+ Git account + +
- + +
+
+
-
-
- -
-
-
); From 207b32ce65afc30c7354ddf1d14b80896c16c577 Mon Sep 17 00:00:00 2001 From: Andre H Date: Fri, 1 Mar 2024 11:23:38 +0800 Subject: [PATCH 14/37] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20integrate=20?= =?UTF-8?q?useform?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projects/create/template/index.tsx | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx index 8f8a0f3..8364f60 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { useForm, SubmitHandler } from 'react-hook-form'; +import { useForm, SubmitHandler, Controller } from 'react-hook-form'; import { useNavigate, useOutletContext, useParams } from 'react-router-dom'; import toast from 'react-hot-toast'; import assert from 'assert'; @@ -10,7 +10,7 @@ import { Template } from '../../../../../types'; import { Heading } from 'components/shared/Heading'; import { Input } from 'components/shared/Input'; import { Radio } from 'components/shared/Radio'; -import { Select } from 'components/shared/Select'; +import { Select, SelectOption } from 'components/shared/Select'; import { ArrowRightCircleFilledIcon, GitIcon, @@ -40,12 +40,12 @@ const CreateRepo = () => { const FRAMEWORK_OPTIONS = [ { - value: 'react-native', + value: 'React', label: 'React Native', leftIcon: , }, { - value: 'expo', + value: 'Expo', label: 'Expo', leftIcon: , }, @@ -114,7 +114,7 @@ const CreateRepo = () => { fetchUserAndOrgs(); }, [octokit]); - const { handleSubmit, reset } = useForm({ + const { handleSubmit, control, reset } = useForm({ defaultValues: { framework: 'React', repoName: '', @@ -140,29 +140,61 @@ const CreateRepo = () => {
Framework - ( + + )} />
Git account - onChange((value as SelectOption).value)} + options={ + gitAccounts.map((account) => ({ + value: account, + label: account, + })) ?? [] + } + /> + )} />
Name the repo - + ( + + )} + />
- + ( + + )} + />
- - +
diff --git a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx index 81cdbe1..c133704 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx @@ -115,8 +115,8 @@ const OverviewTabPanel = () => { }, [project]); return ( -
-
+
+
{ imageSrc={project.icon} type="blue" /> -
- +
+ {project.name} - +

{project.subDomain} - +

}> From 2182beca22c86a096ba6533f7205b51a09ff1612 Mon Sep 17 00:00:00 2001 From: Andre H Date: Tue, 5 Mar 2024 14:22:59 +0800 Subject: [PATCH 36/37] =?UTF-8?q?=F0=9F=94=A7=20chore:=20remove=20database?= =?UTF-8?q?=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/org-slug/projects/Id.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/frontend/src/pages/org-slug/projects/Id.tsx b/packages/frontend/src/pages/org-slug/projects/Id.tsx index 668b3f8..9db6dee 100644 --- a/packages/frontend/src/pages/org-slug/projects/Id.tsx +++ b/packages/frontend/src/pages/org-slug/projects/Id.tsx @@ -107,9 +107,6 @@ const Id = () => { Deployments - - Database - Integrations From 8c0162f9f354ae8aa53472898ad526032433e884 Mon Sep 17 00:00:00 2001 From: Andre H Date: Wed, 6 Mar 2024 09:35:18 +0800 Subject: [PATCH 37/37] =?UTF-8?q?=F0=9F=94=A7=20chore:=20replace=20Avatar?= =?UTF-8?q?=20with=20TemplateIcon=20on=20Template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/org-slug/projects/create/Template.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/org-slug/projects/create/Template.tsx b/packages/frontend/src/pages/org-slug/projects/create/Template.tsx index b8ee2f8..3d61792 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/Template.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/Template.tsx @@ -7,8 +7,11 @@ import { } from 'react-router-dom'; import templates from '../../../../assets/templates'; -import { Avatar } from 'components/shared/Avatar'; -import { LinkChainIcon } from 'components/shared/CustomIcon'; +import { + LinkChainIcon, + TemplateIcon, + TemplateIconType, +} from 'components/shared/CustomIcon'; import { Heading } from 'components/shared/Heading'; import { Steps } from 'components/shared/Steps'; @@ -47,7 +50,7 @@ const CreateWithTemplate = () => {
- + {template?.name}