From 99eb514306e38239cbc2b222cdd3b647bc332b78 Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Wed, 28 Feb 2024 21:16:26 +0700 Subject: [PATCH 01/28] =?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 f09e61d6..171082ed 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 00000000..dc3bc8c5 --- /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 1c86ee3b..49eeed96 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/28] =?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 fbcf50b4..00000000 --- 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 00000000..6c8363ef --- /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 00000000..78472497 --- /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/28] =?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 b7c549fd..b26ff856 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/28] =?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 b1ecb705..0144d578 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 fb3fd7d6..dd88bd80 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/28] =?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 43d4b0f4..fed6531e 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 963cc0bb..8c7e9149 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/28] =?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 171082ed..14af57e6 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 8c7e9149..0e82edcc 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/28] =?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 6c8363ef..b06fc233 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/28] =?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 0e82edcc..fa22428c 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 1ff5ab3dfdba9dcf5dd1cb0f9435bd863a6d0340 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Thu, 29 Feb 2024 18:33:34 +0530 Subject: [PATCH 09/28] Assign project domain to latest production deployment (#145) * Create ApplicationDeploymentRequest with project DNS * Add comment for filtering AppDeploymentRecord * Fix lint * Handle delete gitHub event * Add sleep before registry write methods --------- Co-authored-by: neeraj --- packages/backend/src/registry.ts | 19 +++++++++---- packages/backend/src/service.ts | 49 +++++++++++++++++++------------- packages/backend/src/types.ts | 1 + packages/backend/src/utils.ts | 2 ++ 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 8a6e489b..ed30af9f 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -12,12 +12,14 @@ import { ApplicationDeploymentRequest } from './entity/Deployment'; import { AppDeploymentRecord, PackageJSON } from './types'; +import { sleep } from './utils'; const log = debug('snowball:registry'); const APP_RECORD_TYPE = 'ApplicationRecord'; const APP_DEPLOYMENT_REQUEST_TYPE = 'ApplicationDeploymentRequest'; const APP_DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRecord'; +const SLEEP_DURATION = 1000; // TODO: Move registry code to laconic-sdk/watcher-ts export class Registry { @@ -111,16 +113,21 @@ export class Registry { const crn = this.getCrn(appName); log(`Setting name: ${crn} for record ID: ${result.data.id}`); + await sleep(SLEEP_DURATION); await this.registry.setName( { cid: result.data.id, crn }, this.registryConfig.privateKey, this.registryConfig.fee ); + + await sleep(SLEEP_DURATION); await this.registry.setName( { cid: result.data.id, crn: `${crn}@${applicationRecord.app_version}` }, this.registryConfig.privateKey, this.registryConfig.fee ); + + await sleep(SLEEP_DURATION); await this.registry.setName( { cid: result.data.id, @@ -139,9 +146,9 @@ export class Registry { async createApplicationDeploymentRequest (data: { deployment: Deployment, appName: string, - packageJsonName: string, repository: string, - environmentVariables: { [key: string]: string } + environmentVariables: { [key: string]: string }, + dns: string, }): Promise<{ applicationDeploymentRequestId: string; applicationDeploymentRequestData: ApplicationDeploymentRequest; @@ -160,7 +167,7 @@ export class Registry { version: '1.0.0', name: `${applicationRecord.attributes.name}@${applicationRecord.attributes.app_version}`, application: `${crn}@${applicationRecord.attributes.app_version}`, - dns: `${data.deployment.project.name}-${data.deployment.id}`, + dns: data.dns, // TODO: Not set in test-progressive-web-app CI // deployment: '$CERC_REGISTRY_DEPLOYMENT_CRN', @@ -178,6 +185,7 @@ export class Registry { }) }; + await sleep(SLEEP_DURATION); const result = await this.registry.setRecord( { privateKey: this.registryConfig.privateKey, @@ -211,11 +219,12 @@ export class Registry { true ); - // Filter records with ApplicationRecord ids + // Filter records with ApplicationRecord ID and Deployment specific URL return records.filter((record: AppDeploymentRecord) => deployments.some( (deployment) => - deployment.applicationRecordId === record.attributes.application + deployment.applicationRecordId === record.attributes.application && + record.attributes.url.includes(deployment.id) ) ); } diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index d204537c..5211585c 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -382,8 +382,7 @@ export class Service { async createDeployment ( userId: string, octokit: Octokit, - data: DeepPartial, - recordData: { repoUrl?: string } = {} + data: DeepPartial ): Promise { assert(data.project?.repository, 'Project repository not found'); log( @@ -407,13 +406,10 @@ export class Service { assert(packageJSON.name, "name field doesn't exist in package.json"); - if (!recordData.repoUrl) { - const { data: repoDetails } = await octokit.rest.repos.get({ - owner, - repo - }); - recordData.repoUrl = repoDetails.html_url; - } + const repoUrl = (await octokit.rest.repos.get({ + owner, + repo + })).data.html_url; // TODO: Set environment variables for each deployment (environment variables can`t be set in application record) const { applicationRecordId, applicationRecordData } = @@ -422,7 +418,7 @@ export class Service { packageJSON, appType: data.project!.template!, commitHash: data.commitHash!, - repoUrl: recordData.repoUrl + repoUrl }); // Update previous deployment with prod branch domain @@ -464,11 +460,23 @@ export class Service { { deployment: newDeployment, appName: repo, - packageJsonName: packageJSON.name, - repository: recordData.repoUrl, - environmentVariables: environmentVariablesObj + repository: repoUrl, + environmentVariables: environmentVariablesObj, + dns: `${newDeployment.project.name}-${newDeployment.id}` }); + // To set project DNS + if (data.environment === Environment.Production) { + await this.registry.createApplicationDeploymentRequest( + { + deployment: newDeployment, + appName: repo, + repository: repoUrl, + environmentVariables: environmentVariablesObj, + dns: `${newDeployment.project.name}` + }); + } + await this.db.updateDeploymentById(newDeployment.id, { applicationDeploymentRequestId, applicationDeploymentRequestData }); return newDeployment; @@ -498,8 +506,6 @@ export class Service { per_page: 1 }); - const { data: repoDetails } = await octokit.rest.repos.get({ owner, repo }); - // Create deployment with prod branch and latest commit await this.createDeployment(user.id, octokit, @@ -510,9 +516,6 @@ export class Service { domain: null, commitHash: latestCommit.sha, commitMessage: latestCommit.commit.message - }, - { - repoUrl: repoDetails.html_url } ); @@ -555,8 +558,14 @@ export class Service { } async handleGitHubPush (data: GitPushEventPayload): Promise { - const { repository, ref, head_commit: headCommit } = data; - log(`Handling GitHub push event from repository: ${repository.full_name}`); + const { repository, ref, head_commit: headCommit, deleted } = data; + + if (deleted) { + log(`Branch ${ref} deleted for project ${repository.full_name}`); + return; + } + + log(`Handling GitHub push event from repository: ${repository.full_name}, branch: ${ref}`); const projects = await this.db.getProjects({ where: { repository: repository.full_name } }); diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index eae6929f..80941346 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -24,6 +24,7 @@ export interface GitPushEventPayload { id: string; message: string; }; + deleted: boolean; } export interface AppDeploymentRecordAttributes { diff --git a/packages/backend/src/utils.ts b/packages/backend/src/utils.ts index 693223bb..8d2a8e8b 100644 --- a/packages/backend/src/utils.ts +++ b/packages/backend/src/utils.ts @@ -66,3 +66,5 @@ export const loadAndSaveData = async ( return savedEntity; }; + +export const sleep = async (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms)); From d64cf46d895a96337523e73018d0f6d36aa1425c Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Thu, 29 Feb 2024 22:29:23 +0530 Subject: [PATCH 10/28] Fetch latest registry record version and use in deploy script (#146) * Fetch latest registry record version and increment * Move reference up * Remove steps to manually update record values * Update registry config with new key and bond --- packages/deployer/README.md | 51 ------------------- packages/deployer/config.yml | 2 + packages/deployer/deploy-frontend.sh | 30 +++++------ .../application-deployment-request.yml | 4 +- .../deployer/records/application-record.yml | 4 +- 5 files changed, 21 insertions(+), 70 deletions(-) diff --git a/packages/deployer/README.md b/packages/deployer/README.md index 04c5d6b5..d2976852 100644 --- a/packages/deployer/README.md +++ b/packages/deployer/README.md @@ -8,57 +8,6 @@ brew install jq # if you do not have jq installed already ``` - - - -Example of how to make the necessary deploy edits [here](https://github.com/snowball-tools/snowballtools-base/pull/131/files). - -- Replace variables in the following files - - [records/application-deployment-request.yml](records/application-deployment-request.yml) - - update the name & application version numbers - - ``: Replace with current time which can be generated by command `date -u` - ```yml - # Example - record: - ... - meta: - note: Added by Snowball @ Friday 23 February 2024 06:35:50 AM UTC - ... - ``` - -- Update record version in [records/application-record.yml](records/application-record.yml) - ```yml - record: - type: ApplicationRecord - version: - ... - ``` - -- Update commit hash in the following places: - - [records/application-record.yml](records/application-record.yml) - ```yml - record: - ... - repository_ref: - ... - ``` - - [records/application-deployment-request.yml](records/application-deployment-request.yml) - ```yml - record: - ... - meta: - ... - repository_ref: - ``` - - [deploy-frontend.sh](deploy-frontend.sh) - Also be sure to update the app version - ```bash - ... - RCD_APP_VERSION="" - REPO_REF="" - ... - ``` - - Run script to deploy app ``` ./deploy-frontend.sh diff --git a/packages/deployer/config.yml b/packages/deployer/config.yml index f65a3bfc..b29f5178 100644 --- a/packages/deployer/config.yml +++ b/packages/deployer/config.yml @@ -2,6 +2,8 @@ services: cns: restEndpoint: http://console.laconic.com:1317 gqlEndpoint: http://console.laconic.com:9473/api + userKey: 489c9dd3931c2a2d4dd77973302dc5eb01e2a49552f9d932c58d9da823512311 + bondId: 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32 chainId: laconic_9000-1 gas: 1200000 fees: 200000aphoton diff --git a/packages/deployer/deploy-frontend.sh b/packages/deployer/deploy-frontend.sh index 3bff6698..92510428 100755 --- a/packages/deployer/deploy-frontend.sh +++ b/packages/deployer/deploy-frontend.sh @@ -12,18 +12,17 @@ PACKAGE_VERSION=$(jq -r '.version' ../frontend/package.json) # Current date and time for note CURRENT_DATE_TIME=$(date -u) -# Increment application-record version -APPLICATION_RECORD_FILE="./records/application-record.yml" -if [ -f "$APPLICATION_RECORD_FILE" ]; then - # Extract current version and increment it - CURRENT_VERSION=$(grep 'version:' $APPLICATION_RECORD_FILE | head -1 | awk '{print $2}') - IFS='.' read -ra ADDR <<< "$CURRENT_VERSION" - VERSION_NUMBER=${ADDR[2]} - NEW_VERSION_NUMBER=$((VERSION_NUMBER + 1)) - NEW_APPLICATION_VERSION="${ADDR[0]}.${ADDR[1]}.$NEW_VERSION_NUMBER" -else - # If file does not exist, start from version 0.0.1 - NEW_APPLICATION_VERSION="0.0.1" +CONFIG_FILE=config.yml +REGISTRY_BOND_ID="8fcf44b2f326b4b63ac57547777f1c78b7d494e5966e508f09001af53cb440ac" + +# Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts + +# Get latest version from registry and increment application-record version +NEW_APPLICATION_VERSION=$(yarn --silent laconic -c $CONFIG_FILE cns record list --type ApplicationRecord --all --name "snowballtools-base-frontend" 2>/dev/null | jq -r -s ".[] | sort_by(.createTime) | reverse | [ .[] | select(.bondId == \"$REGISTRY_BOND_ID\") ] | .[0].attributes.version" | awk -F. -v OFS=. '{$NF += 1 ; print}') + +if [ -z "$NEW_APPLICATION_VERSION" ] || [ "1" == "$NEW_APPLICATION_VERSION" ]; then + # Set application-record version if no previous records were found + NEW_APPLICATION_VERSION=0.0.1 fi # Generate application-deployment-request.yml @@ -62,10 +61,7 @@ EOF echo "Files generated successfully." -# Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts - RECORD_FILE=records/application-record.yml -CONFIG_FILE=config.yml # Publish ApplicationRecord RECORD_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id') @@ -75,8 +71,11 @@ echo $RECORD_ID # Set name to record REGISTRY_APP_CRN="crn://snowballtools/applications/snowballtools-base-frontend" +sleep 2 yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${PACKAGE_VERSION}" "$RECORD_ID" +sleep 2 yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${LATEST_HASH}" "$RECORD_ID" +sleep 2 # Set name if latest release yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN" "$RECORD_ID" echo "$REGISTRY_APP_CRN set for ApplicationRecord" @@ -90,6 +89,7 @@ fi RECORD_FILE=records/application-deployment-request.yml +sleep 2 DEPLOYMENT_REQUEST_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id') echo "ApplicationDeploymentRequest published" echo $DEPLOYMENT_REQUEST_ID diff --git a/packages/deployer/records/application-deployment-request.yml b/packages/deployer/records/application-deployment-request.yml index a543e47a..8551abf7 100644 --- a/packages/deployer/records/application-deployment-request.yml +++ b/packages/deployer/records/application-deployment-request.yml @@ -13,6 +13,6 @@ record: LACONIC_HOSTED_CONFIG_app_github_image_upload_templaterepo: snowball-tools-platform/image-upload-pwa-example LACONIC_HOSTED_CONFIG_app_wallet_connect_id: eda9ba18042a5ea500f358194611ece2 meta: - note: Added by Snowball @ Thu Feb 29 02:48:23 UTC 2024 + note: Added by Snowball @ Thursday 29 February 2024 04:36:04 PM UTC repository: "https://git.vdb.to/cerc-io/snowballtools-base" - repository_ref: 94a9bf88a9cf8442ffd37ad924cf9a590af69431 + repository_ref: 1ff5ab3dfdba9dcf5dd1cb0f9435bd863a6d0340 diff --git a/packages/deployer/records/application-record.yml b/packages/deployer/records/application-record.yml index c5b718f6..eceed263 100644 --- a/packages/deployer/records/application-record.yml +++ b/packages/deployer/records/application-record.yml @@ -1,7 +1,7 @@ record: type: ApplicationRecord - version: 0.0.25 - repository_ref: 94a9bf88a9cf8442ffd37ad924cf9a590af69431 + version: 0.0.1 + repository_ref: 1ff5ab3dfdba9dcf5dd1cb0f9435bd863a6d0340 repository: ["https://git.vdb.to/cerc-io/snowballtools-base"] app_type: webapp name: snowballtools-base-frontend From 65f64a3dcdb30852d4bed1c4afba06cb7911bc05 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Thu, 29 Feb 2024 22:45:33 +0530 Subject: [PATCH 11/28] Fix frontend deployment config (#148) --- packages/deployer/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/deployer/config.yml b/packages/deployer/config.yml index b29f5178..4eeaf9a5 100644 --- a/packages/deployer/config.yml +++ b/packages/deployer/config.yml @@ -7,5 +7,3 @@ services: chainId: laconic_9000-1 gas: 1200000 fees: 200000aphoton - userKey: 0524fc22ea0a12e6c5cc4cfe08e73c95dffd0ab5ed72a59f459ed33134fa3b16 - bondId: 8fcf44b2f326b4b63ac57547777f1c78b7d494e5966e508f09001af53cb440ac From 409b654f9b1757de32639127ecd75c2275bf7cf3 Mon Sep 17 00:00:00 2001 From: Sushan Yadav <53527664+sushanyadav@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:05:50 +0545 Subject: [PATCH 12/28] Project Deployments - Deployed line items (#147) * feat: add deployment lines * fix: typo DeploymentMenu --- .vscode/settings.json | 3 +- .../deployments/DeploymentDetailsCard.tsx | 349 ++++++------------ .../project/deployments/DeploymentMenu.tsx | 268 ++++++++++++++ .../project/deployments/FilterForm.tsx | 2 +- .../shared/CustomIcon/CommitIcon.tsx | 26 ++ .../shared/CustomIcon/RefreshIcon.tsx | 41 ++ .../shared/CustomIcon/RocketIcon.tsx | 20 + .../components/shared/CustomIcon/UndoIcon.tsx | 21 ++ .../src/components/shared/CustomIcon/index.ts | 4 + .../shared/OverflownText/OverflownText.tsx | 71 ++++ .../components/shared/OverflownText/index.ts | 1 + .../src/components/shared/Tag/Tag.theme.ts | 2 +- .../org-slug/projects/id/Deployments.tsx | 8 +- packages/frontend/tailwind.config.js | 3 + 14 files changed, 566 insertions(+), 253 deletions(-) create mode 100644 packages/frontend/src/components/projects/project/deployments/DeploymentMenu.tsx create mode 100644 packages/frontend/src/components/shared/CustomIcon/CommitIcon.tsx create mode 100644 packages/frontend/src/components/shared/CustomIcon/RefreshIcon.tsx create mode 100644 packages/frontend/src/components/shared/CustomIcon/RocketIcon.tsx create mode 100644 packages/frontend/src/components/shared/CustomIcon/UndoIcon.tsx create mode 100644 packages/frontend/src/components/shared/OverflownText/OverflownText.tsx create mode 100644 packages/frontend/src/components/shared/OverflownText/index.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index f3e08235..c831fde1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { // IntelliSense for taiwind variants "tailwindCSS.experimental.classRegex": [ - ["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] + "tv\\('([^)]*)\\')", + "(?:'|\"|`)([^\"'`]*)(?:'|\"|`)" ] } diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx index e191fe8c..c4940bac 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx @@ -1,31 +1,28 @@ -import React, { useState } from 'react'; -import toast from 'react-hot-toast'; +import React from 'react'; import { + Deployment, + DeploymentStatus, + Domain, Environment, Project, - Domain, - DeploymentStatus, - Deployment, } from 'gql-client'; - +import { Avatar } from 'components/shared/Avatar'; import { - Menu, - MenuHandler, - MenuList, - MenuItem, - Typography, - Chip, - ChipProps, - Tooltip, -} from '@material-tailwind/react'; - -import { relativeTimeMs } from '../../../../utils/time'; -import ConfirmDialog from '../../../shared/ConfirmDialog'; -import DeploymentDialogBodyCard from './DeploymentDialogBodyCard'; -import AssignDomainDialog from './AssignDomainDialog'; -import { useGQLClient } from '../../../../context/GQLClientContext'; + BranchStrokeIcon, + CheckRoundFilledIcon, + ClockOutlineIcon, + CommitIcon, + LoadingIcon, + WarningIcon, +} from 'components/shared/CustomIcon'; +import { Heading } from 'components/shared/Heading'; +import { OverflownText } from 'components/shared/OverflownText'; +import { Tag, TagTheme } from 'components/shared/Tag'; +import { getInitials } from 'utils/geInitials'; +import { relativeTimeMs } from 'utils/time'; import { SHORT_COMMIT_HASH_LENGTH } from '../../../../constants'; import { formatAddress } from '../../../../utils/format'; +import { DeploymentMenu } from './DeploymentMenu'; interface DeployDetailsCardProps { deployment: Deployment; @@ -35,10 +32,12 @@ interface DeployDetailsCardProps { prodBranchDomains: Domain[]; } -const STATUS_COLORS: { [key in DeploymentStatus]: ChipProps['color'] } = { - [DeploymentStatus.Building]: 'blue', - [DeploymentStatus.Ready]: 'green', - [DeploymentStatus.Error]: 'red', +const STATUS_COLORS: { + [key in DeploymentStatus]: TagTheme['type']; +} = { + [DeploymentStatus.Building]: 'emphasized', + [DeploymentStatus.Ready]: 'positive', + [DeploymentStatus.Error]: 'negative', }; const DeploymentDetailsCard = ({ @@ -48,241 +47,99 @@ const DeploymentDetailsCard = ({ project, prodBranchDomains, }: DeployDetailsCardProps) => { - const client = useGQLClient(); - - const [changeToProduction, setChangeToProduction] = useState(false); - const [redeployToProduction, setRedeployToProduction] = useState(false); - const [rollbackDeployment, setRollbackDeployment] = useState(false); - const [assignDomainDialog, setAssignDomainDialog] = useState(false); - - const updateDeployment = async () => { - const isUpdated = await client.updateDeploymentToProd(deployment.id); - if (isUpdated) { - await onUpdate(); - toast.success('Deployment changed to production'); - } else { - toast.error('Unable to change deployment to production'); + const getIconByDeploymentStatus = (status: DeploymentStatus) => { + if (status === DeploymentStatus.Building) { + return ; } - }; - - const redeployToProd = async () => { - const isRedeployed = await client.redeployToProd(deployment.id); - if (isRedeployed) { - await onUpdate(); - toast.success('Redeployed to production'); - } else { - toast.error('Unable to redeploy to production'); + if (status === DeploymentStatus.Ready) { + return ; } - }; - const rollbackDeploymentHandler = async () => { - const isRollbacked = await client.rollbackDeployment( - project.id, - deployment.id, - ); - if (isRollbacked) { - await onUpdate(); - toast.success('Deployment rolled back'); - } else { - toast.error('Unable to rollback deployment'); + if (status === DeploymentStatus.Error) { + return ; } }; return ( -
-
-
- {deployment.url && ( - +
+
+ {/* DEPLOYMENT URL */} + {deployment.url && ( + + {deployment.url} - - )} -
- + + + )} + {deployment.environment === Environment.Production ? `Production ${deployment.isCurrent ? '(Current)' : ''}` : 'Preview'} - +
-
- ^} + + {/* DEPLOYMENT STATUS */} +
+ + {deployment.status} + +
+ + {/* DEPLOYMENT COMMIT DETAILS */} +
+ + + {deployment.branch} + + + + + {deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '} + {deployment.commitMessage} + + +
+ + {/* DEPLOYMENT INFOs */} +
+
+ + + {relativeTimeMs(deployment.createdAt)} + +
+ +
+ + + {formatAddress(deployment.createdBy.name ?? '')} + +
+
-
- - ^ {deployment.branch} - - - ^ {deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '} - {deployment.commitMessage} - -
-
- - ^ {relativeTimeMs(deployment.createdAt)} ^{' '} - - {formatAddress(deployment.createdBy.name ?? '')} - - - - - - - - - - ^ Visit - - - setAssignDomainDialog(!assignDomainDialog)} - placeholder={''} - > - ^ Assign domain - - setChangeToProduction(!changeToProduction)} - disabled={!(deployment.environment !== Environment.Production)} - placeholder={''} - > - ^ Change to production - -
- setRedeployToProduction(!redeployToProduction)} - disabled={ - !( - deployment.environment === Environment.Production && - deployment.isCurrent - ) - } - placeholder={''} - > - ^ Redeploy to production - - setRollbackDeployment(!rollbackDeployment)} - disabled={ - deployment.isCurrent || - deployment.environment !== Environment.Production || - !Boolean(currentDeployment) - } - placeholder={''} - > - ^ Rollback to this version - -
-
-
- setChangeToProduction((preVal) => !preVal)} - open={changeToProduction} - confirmButtonTitle="Change" - color="blue" - handleConfirm={async () => { - await updateDeployment(); - setChangeToProduction((preVal) => !preVal); - }} - > -
- - Upon confirmation, this deployment will be changed to production. - - - - The new deployment will be associated with these domains: - - {prodBranchDomains.length > 0 && - prodBranchDomains.map((value) => { - return ( - - ^ {value.name} - - ); - })} -
-
- setRedeployToProduction((preVal) => !preVal)} - open={redeployToProduction} - confirmButtonTitle="Redeploy" - color="blue" - handleConfirm={async () => { - await redeployToProd(); - setRedeployToProduction((preVal) => !preVal); - }} - > -
- - Upon confirmation, new deployment will be created with the same - source code as current deployment. - - - - These domains will point to your new deployment: - - {deployment.domain?.name && ( - - {deployment.domain?.name} - - )} -
-
- {Boolean(currentDeployment) && ( - setRollbackDeployment((preVal) => !preVal)} - open={rollbackDeployment} - confirmButtonTitle="Rollback" - color="blue" - handleConfirm={async () => { - await rollbackDeploymentHandler(); - setRollbackDeployment((preVal) => !preVal); - }} - > -
- - Upon confirmation, this deployment will replace your current - deployment - - - - - These domains will point to your new deployment: - - - ^ {currentDeployment.domain?.name} - -
-
- )} - setAssignDomainDialog(!assignDomainDialog)} - />
); }; diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentMenu.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentMenu.tsx new file mode 100644 index 00000000..f4eb9808 --- /dev/null +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentMenu.tsx @@ -0,0 +1,268 @@ +import React, { useState } from 'react'; +import toast from 'react-hot-toast'; +import { Deployment, Domain, Environment, Project } from 'gql-client'; +import { Button } from 'components/shared/Button'; +import { + GlobeIcon, + HorizontalDotIcon, + LinkIcon, + RefreshIcon, + RocketIcon, + UndoIcon, +} from 'components/shared/CustomIcon'; +import { + Menu, + MenuHandler, + MenuItem, + MenuList, +} from '@material-tailwind/react'; +import { ComponentPropsWithRef } from 'react'; +import ConfirmDialog from '../../../shared/ConfirmDialog'; +import AssignDomainDialog from './AssignDomainDialog'; +import DeploymentDialogBodyCard from './DeploymentDialogBodyCard'; +import { Typography } from '@material-tailwind/react'; +import { useGQLClient } from '../../../../context/GQLClientContext'; +import { cn } from 'utils/classnames'; + +interface DeploymentMenuProps extends ComponentPropsWithRef<'div'> { + deployment: Deployment; + currentDeployment: Deployment; + onUpdate: () => Promise; + project: Project; + prodBranchDomains: Domain[]; +} + +export const DeploymentMenu = ({ + deployment, + currentDeployment, + onUpdate, + project, + prodBranchDomains, + className, + ...props +}: DeploymentMenuProps) => { + const client = useGQLClient(); + + const [changeToProduction, setChangeToProduction] = useState(false); + const [redeployToProduction, setRedeployToProduction] = useState(false); + const [rollbackDeployment, setRollbackDeployment] = useState(false); + const [assignDomainDialog, setAssignDomainDialog] = useState(false); + + const updateDeployment = async () => { + const isUpdated = await client.updateDeploymentToProd(deployment.id); + if (isUpdated) { + await onUpdate(); + toast.success('Deployment changed to production'); + } else { + toast.error('Unable to change deployment to production'); + } + }; + + const redeployToProd = async () => { + const isRedeployed = await client.redeployToProd(deployment.id); + if (isRedeployed) { + await onUpdate(); + toast.success('Redeployed to production'); + } else { + toast.error('Unable to redeploy to production'); + } + }; + + const rollbackDeploymentHandler = async () => { + const isRollbacked = await client.rollbackDeployment( + project.id, + deployment.id, + ); + if (isRollbacked) { + await onUpdate(); + toast.success('Deployment rolled back'); + } else { + toast.error('Unable to rollback deployment'); + } + }; + + return ( + <> +
+ + + +
+ {/* Dialogs */} + setChangeToProduction((preVal) => !preVal)} + open={changeToProduction} + confirmButtonTitle="Change" + color="blue" + handleConfirm={async () => { + await updateDeployment(); + setChangeToProduction((preVal) => !preVal); + }} + > +
+ + Upon confirmation, this deployment will be changed to production. + + + + The new deployment will be associated with these domains: + + {prodBranchDomains.length > 0 && + prodBranchDomains.map((value) => { + return ( + + ^ {value.name} + + ); + })} +
+
+ setRedeployToProduction((preVal) => !preVal)} + open={redeployToProduction} + confirmButtonTitle="Redeploy" + color="blue" + handleConfirm={async () => { + await redeployToProd(); + setRedeployToProduction((preVal) => !preVal); + }} + > +
+ + Upon confirmation, new deployment will be created with the same + source code as current deployment. + + + + These domains will point to your new deployment: + + {deployment.domain?.name && ( + + {deployment.domain?.name} + + )} +
+
+ {Boolean(currentDeployment) && ( + setRollbackDeployment((preVal) => !preVal)} + open={rollbackDeployment} + confirmButtonTitle="Rollback" + color="blue" + handleConfirm={async () => { + await rollbackDeploymentHandler(); + setRollbackDeployment((preVal) => !preVal); + }} + > +
+ + Upon confirmation, this deployment will replace your current + deployment + + + + + These domains will point to your new deployment: + + + ^ {currentDeployment.domain?.name} + +
+
+ )} + setAssignDomainDialog(!assignDomainDialog)} + /> + + ); +}; diff --git a/packages/frontend/src/components/projects/project/deployments/FilterForm.tsx b/packages/frontend/src/components/projects/project/deployments/FilterForm.tsx index c28c6218..dd549c57 100644 --- a/packages/frontend/src/components/projects/project/deployments/FilterForm.tsx +++ b/packages/frontend/src/components/projects/project/deployments/FilterForm.tsx @@ -44,7 +44,7 @@ const FilterForm = ({ value, onChange }: FilterFormProps) => { }, [value]); return ( -
+
{ + return ( + + + + + + + + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/RefreshIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/RefreshIcon.tsx new file mode 100644 index 00000000..3873c371 --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/RefreshIcon.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const RefreshIcon = (props: CustomIconProps) => { + return ( + + + + + + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/RocketIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/RocketIcon.tsx new file mode 100644 index 00000000..488fba8f --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/RocketIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const RocketIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/UndoIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/UndoIcon.tsx new file mode 100644 index 00000000..713a0b6e --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/UndoIcon.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const UndoIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts index b9965188..61239304 100644 --- a/packages/frontend/src/components/shared/CustomIcon/index.ts +++ b/packages/frontend/src/components/shared/CustomIcon/index.ts @@ -41,6 +41,10 @@ export * from './BranchStrokeIcon'; export * from './StorageIcon'; export * from './LinkIcon'; export * from './CursorBoxIcon'; +export * from './CommitIcon'; +export * from './RocketIcon'; +export * from './RefreshIcon'; +export * from './UndoIcon'; // Templates export * from './templates'; diff --git a/packages/frontend/src/components/shared/OverflownText/OverflownText.tsx b/packages/frontend/src/components/shared/OverflownText/OverflownText.tsx new file mode 100644 index 00000000..b03ea580 --- /dev/null +++ b/packages/frontend/src/components/shared/OverflownText/OverflownText.tsx @@ -0,0 +1,71 @@ +import { cn } from 'utils/classnames'; +import { Tooltip, TooltipProps } from 'components/shared/Tooltip'; +import { debounce } from 'lodash'; +import React, { + ComponentPropsWithRef, + PropsWithChildren, + useRef, + useState, + useEffect, +} from 'react'; +import { PolymorphicProps } from 'types/common'; + +interface OverflownTextProps extends ComponentPropsWithRef<'span'> { + tooltipProps?: TooltipProps; + content?: string; +} + +type ElementType = 'span' | 'div'; + +// This component is used to truncate text and show a tooltip if the text is overflown. +export const OverflownText = ({ + tooltipProps, + children, + content, + className, + as, + ...props +}: PropsWithChildren>) => { + const ref = useRef(null); + const [isOverflown, setIsOverflown] = useState(false); + + useEffect(() => { + const element = ref.current as HTMLElement | null; + if (!element) return; + + setIsOverflown(element.scrollWidth > element.clientWidth); + + const handleResize = () => { + const isOverflown = element.scrollWidth > element.clientWidth; + setIsOverflown(isOverflown); + }; + + window.addEventListener('resize', debounce(handleResize, 500)); + + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + const Component = as || 'span'; + + return ( + + + {children} + + + ); +}; diff --git a/packages/frontend/src/components/shared/OverflownText/index.ts b/packages/frontend/src/components/shared/OverflownText/index.ts new file mode 100644 index 00000000..1ffb16f2 --- /dev/null +++ b/packages/frontend/src/components/shared/OverflownText/index.ts @@ -0,0 +1 @@ +export * from './OverflownText'; diff --git a/packages/frontend/src/components/shared/Tag/Tag.theme.ts b/packages/frontend/src/components/shared/Tag/Tag.theme.ts index a027185d..884f79bc 100644 --- a/packages/frontend/src/components/shared/Tag/Tag.theme.ts +++ b/packages/frontend/src/components/shared/Tag/Tag.theme.ts @@ -4,7 +4,7 @@ import type { VariantProps } from 'tailwind-variants'; export const tagTheme = tv( { slots: { - wrapper: ['flex', 'gap-1.5', 'rounded-lg', 'border'], + wrapper: ['inline-flex', 'gap-1.5', 'rounded-lg', 'border'], icon: [], label: ['font-inter', 'text-xs'], }, diff --git a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx index d26a2dd4..d5f0346f 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx @@ -88,17 +88,17 @@ const DeploymentsTabPanel = () => { setFilterValue(DEFAULT_FILTER_VALUE); }, []); - const onUpdateDeploymenToProd = async () => { + const onUpdateDeploymentToProd = async () => { await fetchDeployments(); }; return ( -
+
setFilterValue(value)} /> -
+
{Boolean(filteredDeployments.length) ? ( filteredDeployments.map((deployment, key) => { return ( @@ -106,7 +106,7 @@ const DeploymentsTabPanel = () => { deployment={deployment} key={key} currentDeployment={currentDeployment!} - onUpdate={onUpdateDeploymenToProd} + onUpdate={onUpdateDeploymentToProd} project={project} prodBranchDomains={prodBranchDomains} /> diff --git a/packages/frontend/tailwind.config.js b/packages/frontend/tailwind.config.js index c362e898..4efc11fe 100644 --- a/packages/frontend/tailwind.config.js +++ b/packages/frontend/tailwind.config.js @@ -12,6 +12,9 @@ export default withMT({ zIndex: { tooltip: '52', }, + letterSpacing: { + tight: '-0.084px', + }, fontFamily: { sans: ['Inter', 'sans-serif'], display: ['Inter Display', 'sans-serif'], From 4fc654f763cd9353baa1829703d91849396f356b Mon Sep 17 00:00:00 2001 From: Zachery Date: Fri, 1 Mar 2024 15:22:16 +0800 Subject: [PATCH 13/28] [T-4933] Create Project - Mobile modal layout (#151) * feat: create project mobile modal layout * style: make dialog content stretch full height --- packages/frontend/package.json | 1 + .../pages/org-slug/projects/create/layout.tsx | 86 +++++++++++++++---- packages/frontend/tailwind.config.js | 1 + yarn.lock | 21 +++++ 4 files changed, 93 insertions(+), 16 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 204b9f3d..ec89a071 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -8,6 +8,7 @@ "@material-tailwind/react": "^2.1.7", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-switch": "^1.0.3", diff --git a/packages/frontend/src/pages/org-slug/projects/create/layout.tsx b/packages/frontend/src/pages/org-slug/projects/create/layout.tsx index 3dad337a..906a6215 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/layout.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/layout.tsx @@ -6,6 +6,7 @@ import { WavyBorder } from 'components/shared/WavyBorder'; import { Button } from 'components/shared/Button'; import { CrossIcon } from 'components/shared/CustomIcon'; import { cn } from 'utils/classnames'; +import * as Dialog from '@radix-ui/react-dialog'; export interface CreateProjectLayoutProps extends ComponentPropsWithoutRef<'section'> {} @@ -16,24 +17,77 @@ export const CreateProjectLayout = ({ }: CreateProjectLayoutProps) => { const { orgSlug } = useParams(); + const closeBtnLink = `/${orgSlug}`; + + const heading = ( + + Create new project + + ); + return ( -
-
-
- - Create new project - - - - + <> + {/* Desktop */} +
-
- + +
+ +
-
+ + {/* Mobile */} + {/* Setting modal={false} so even if modal is active on desktop, it doesn't block clicks */} + + + {/* Not using since modal={false} disables it and its content will not show */} +
+ + {/* Heading */} +
+ {heading} + + +
+ + {/* Border */} + + + {/* Page content */} +
+ +
+
+
+
+
+ ); }; diff --git a/packages/frontend/tailwind.config.js b/packages/frontend/tailwind.config.js index 4efc11fe..b5fa02b0 100644 --- a/packages/frontend/tailwind.config.js +++ b/packages/frontend/tailwind.config.js @@ -87,6 +87,7 @@ export default withMT({ 900: '#0a3a5c', }, base: { + canvas: '#ECF6FE', bg: '#ffffff', 'bg-alternate': '#f8fafc', 'bg-emphasized': '#f1f5f9', diff --git a/yarn.lock b/yarn.lock index 60ab1754..e44f8793 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3786,6 +3786,27 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-dialog@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300" + integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-focus-guards" "1.0.1" + "@radix-ui/react-focus-scope" "1.0.4" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-use-controllable-state" "1.0.1" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.5" + "@radix-ui/react-direction@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b" From 64e3aa5b25b34b209b1977e8145a00c73d775561 Mon Sep 17 00:00:00 2001 From: Zachery Date: Sun, 3 Mar 2024 13:41:25 +0800 Subject: [PATCH 14/28] [T-4913] Create Project - Deploy page (#152) --- .../src/components/FormatMilliSecond.tsx | 15 +- .../frontend/src/components/StopWatch.tsx | 10 +- .../src/components/projects/create/Deploy.tsx | 84 ++++++----- .../components/projects/create/DeployStep.tsx | 142 +++++++++++++----- .../components/shared/CustomIcon/CopyIcon.tsx | 21 +++ .../shared/CustomIcon/LoaderIcon.tsx | 22 +++ .../shared/CustomIcon/MinusCircleIcon.tsx | 21 +++ .../src/components/shared/CustomIcon/index.ts | 3 + 8 files changed, 232 insertions(+), 86 deletions(-) create mode 100644 packages/frontend/src/components/shared/CustomIcon/CopyIcon.tsx create mode 100644 packages/frontend/src/components/shared/CustomIcon/LoaderIcon.tsx create mode 100644 packages/frontend/src/components/shared/CustomIcon/MinusCircleIcon.tsx diff --git a/packages/frontend/src/components/FormatMilliSecond.tsx b/packages/frontend/src/components/FormatMilliSecond.tsx index 047a5479..bc8dcda6 100644 --- a/packages/frontend/src/components/FormatMilliSecond.tsx +++ b/packages/frontend/src/components/FormatMilliSecond.tsx @@ -1,13 +1,22 @@ import { Duration } from 'luxon'; -import React from 'react'; +import React, { ComponentPropsWithoutRef } from 'react'; +import { cn } from 'utils/classnames'; -const FormatMillisecond = ({ time }: { time: number }) => { +export interface FormatMilliSecondProps + extends ComponentPropsWithoutRef<'div'> { + time: number; +} + +const FormatMillisecond = ({ time, ...props }: FormatMilliSecondProps) => { const formatTime = Duration.fromMillis(time) .shiftTo('days', 'hours', 'minutes', 'seconds') .toObject(); return ( -
+
{formatTime.days !== 0 && {formatTime.days}d } {formatTime.hours !== 0 && {formatTime.hours}h } {formatTime.minutes !== 0 && {formatTime.minutes}m } diff --git a/packages/frontend/src/components/StopWatch.tsx b/packages/frontend/src/components/StopWatch.tsx index 4e70f9b0..239b1197 100644 --- a/packages/frontend/src/components/StopWatch.tsx +++ b/packages/frontend/src/components/StopWatch.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useStopwatch } from 'react-timer-hook'; -import FormatMillisecond from './FormatMilliSecond'; +import FormatMillisecond, { FormatMilliSecondProps } from './FormatMilliSecond'; const setStopWatchOffset = (time: string) => { const providedTime = new Date(time); @@ -11,13 +11,17 @@ const setStopWatchOffset = (time: string) => { return currentTime; }; -const Stopwatch = ({ offsetTimestamp }: { offsetTimestamp: Date }) => { +interface StopwatchProps extends Omit { + offsetTimestamp: Date; +} + +const Stopwatch = ({ offsetTimestamp, ...props }: StopwatchProps) => { const { totalSeconds } = useStopwatch({ autoStart: true, offsetTimestamp: offsetTimestamp, }); - return ; + return ; }; export { Stopwatch, setStopWatchOffset }; diff --git a/packages/frontend/src/components/projects/create/Deploy.tsx b/packages/frontend/src/components/projects/create/Deploy.tsx index 67698959..a251773d 100644 --- a/packages/frontend/src/components/projects/create/Deploy.tsx +++ b/packages/frontend/src/components/projects/create/Deploy.tsx @@ -1,11 +1,14 @@ import React, { useCallback, useEffect } from 'react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { Button, Typography } from '@material-tailwind/react'; +import { Typography } from '@material-tailwind/react'; import { DeployStep, DeployStatus } from './DeployStep'; import { Stopwatch, setStopWatchOffset } from '../../StopWatch'; import ConfirmDialog from 'components/shared/ConfirmDialog'; +import { Heading } from 'components/shared/Heading'; +import { Button } from 'components/shared/Button'; +import { ClockOutlineIcon, WarningIcon } from 'components/shared/CustomIcon'; const TIMEOUT_DURATION = 5000; const Deploy = () => { @@ -31,27 +34,27 @@ const Deploy = () => { }, []); return ( -
-
-
-

Deployment started ...

-
- ^  +
+
+
+ + Deployment started ... + +
+
-
- -
+ {
- - - - + +
+ + + + +
); }; diff --git a/packages/frontend/src/components/projects/create/DeployStep.tsx b/packages/frontend/src/components/projects/create/DeployStep.tsx index 1aae9c0c..71283ddb 100644 --- a/packages/frontend/src/components/projects/create/DeployStep.tsx +++ b/packages/frontend/src/components/projects/create/DeployStep.tsx @@ -1,11 +1,22 @@ import React, { useState } from 'react'; -import toast from 'react-hot-toast'; -import { Collapse, Button, Typography } from '@material-tailwind/react'; +import { Collapse } from '@material-tailwind/react'; import { Stopwatch, setStopWatchOffset } from '../../StopWatch'; import FormatMillisecond from '../../FormatMilliSecond'; import processLogs from '../../../assets/process-logs.json'; +import { cn } from 'utils/classnames'; +import { + CheckRoundFilledIcon, + ClockOutlineIcon, + CopyIcon, + LoaderIcon, + MinusCircleIcon, + PlusIcon, +} from 'components/shared/CustomIcon'; +import { Button } from 'components/shared/Button'; +import { useToast } from 'components/shared/Toast'; +import { useIntersectionObserver } from 'usehooks-ts'; enum DeployStatus { PROCESSING = 'progress', @@ -28,61 +39,110 @@ const DeployStep = ({ startTime, processTime, }: DeployStepsProps) => { - const [collapse, setCollapse] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const { toast, dismiss } = useToast(); + const { isIntersecting: hideGradientOverlay, ref } = useIntersectionObserver({ + threshold: 1, + }); + + const disableCollapse = status !== DeployStatus.COMPLETE; return ( -
-
- {status === DeployStatus.NOT_STARTED &&
{step}
} - {status === DeployStatus.PROCESSING &&
O
} - {status === DeployStatus.COMPLETE && ( -
- +
+ {/* Collapisble trigger */} +
- -
+ + + {/* Collapsible */} + +
+ {/* Logs */} {processLogs.map((log, key) => { return ( - +

{log} - +

); })} -
+ + {/* End of logs ref used for hiding gradient overlay */} +
+ + {/* Overflow gradient overlay */} + {!hideGradientOverlay && ( +
+ )} + + {/* Copy log button */} +
diff --git a/packages/frontend/src/components/shared/CustomIcon/CopyIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/CopyIcon.tsx new file mode 100644 index 00000000..34975bec --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/CopyIcon.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const CopyIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/LoaderIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/LoaderIcon.tsx new file mode 100644 index 00000000..720bfc22 --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/LoaderIcon.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const LoaderIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/MinusCircleIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/MinusCircleIcon.tsx new file mode 100644 index 00000000..7f685677 --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/MinusCircleIcon.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const MinusCircleIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts index 61239304..096761a5 100644 --- a/packages/frontend/src/components/shared/CustomIcon/index.ts +++ b/packages/frontend/src/components/shared/CustomIcon/index.ts @@ -45,6 +45,9 @@ export * from './CommitIcon'; export * from './RocketIcon'; export * from './RefreshIcon'; export * from './UndoIcon'; +export * from './LoaderIcon'; +export * from './MinusCircleIcon'; +export * from './CopyIcon'; // Templates export * from './templates'; From adb64f2db1b15de20b019be7f4af9c2a5c09f2b8 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 10:14:21 +0800 Subject: [PATCH 15/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20remove=20outer=20?= =?UTF-8?q?padding=20on=20the=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shared/CustomIcon/CheckRoundFilledIcon.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/components/shared/CustomIcon/CheckRoundFilledIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/CheckRoundFilledIcon.tsx index 145e68e1..7811a1ec 100644 --- a/packages/frontend/src/components/shared/CustomIcon/CheckRoundFilledIcon.tsx +++ b/packages/frontend/src/components/shared/CustomIcon/CheckRoundFilledIcon.tsx @@ -3,17 +3,11 @@ import { CustomIcon, CustomIconProps } from './CustomIcon'; export const CheckRoundFilledIcon = (props: CustomIconProps) => { return ( - + From 6c42a2297214a3288465cf2bad4c8bdff733f4a7 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 10:38:12 +0800 Subject: [PATCH 16/28] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20implement=20?= =?UTF-8?q?steps/=20timeline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shared/Steps/Step/Step.theme.ts | 43 +++++++++++++ .../src/components/shared/Steps/Step/Step.tsx | 60 +++++++++++++++++++ .../src/components/shared/Steps/Step/index.ts | 2 + .../components/shared/Steps/Steps.theme.ts | 18 ++++++ .../src/components/shared/Steps/Steps.tsx | 42 +++++++++++++ .../src/components/shared/Steps/index.ts | 2 + 6 files changed, 167 insertions(+) create mode 100644 packages/frontend/src/components/shared/Steps/Step/Step.theme.ts create mode 100644 packages/frontend/src/components/shared/Steps/Step/Step.tsx create mode 100644 packages/frontend/src/components/shared/Steps/Step/index.ts create mode 100644 packages/frontend/src/components/shared/Steps/Steps.theme.ts create mode 100644 packages/frontend/src/components/shared/Steps/Steps.tsx create mode 100644 packages/frontend/src/components/shared/Steps/index.ts diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts new file mode 100644 index 00000000..82531ace --- /dev/null +++ b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts @@ -0,0 +1,43 @@ +import { VariantProps, tv } from 'tailwind-variants'; + +export const stepTheme = tv({ + slots: { + wrapper: ['relative', 'px-1.5', 'py-1.5', 'flex', 'gap-2', 'items-center'], + step: [ + 'bg-base-bg-emphasized', + 'rounded-full', + 'w-7', + 'h-7', + 'flex', + 'items-center', + 'justify-center', + 'text-elements-mid-em', + 'shadow-button', + 'shrink-0', + ], + label: ['text-sm', 'font-sans', 'text-elements-mid-em'], + connector: ['bg-border-interactive-hovered'], + }, + variants: { + orientation: { + vertical: { connector: ['w-px', 'h-3', 'ml-5'] }, + horizontal: { connector: ['h-px', 'w-full'] }, + }, + active: { + true: { + step: ['bg-controls-secondary-hovered', 'text-elements-on-secondary'], + label: ['text-elements-high-em'], + }, + }, + completed: { + true: { + step: ['text-controls-primary'], + }, + }, + }, + defaultVariants: { + orientation: 'vertical', + }, +}); + +export type StepTheme = VariantProps; diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.tsx b/packages/frontend/src/components/shared/Steps/Step/Step.tsx new file mode 100644 index 00000000..001ed6b6 --- /dev/null +++ b/packages/frontend/src/components/shared/Steps/Step/Step.tsx @@ -0,0 +1,60 @@ +import React, { useCallback, ComponentPropsWithoutRef } from 'react'; +import { stepTheme, StepTheme } from './Step.theme'; +import { CheckRoundFilledIcon } from 'components/shared/CustomIcon'; + +export interface StepProps extends ComponentPropsWithoutRef<'li'>, StepTheme { + /** + * The label for the step + */ + label: string; + /** + * The index of the step + */ + index: number; + /** + * The total number of steps + */ + currentIndex: number; +} + +export const Step = ({ + label, + index, + currentIndex, + orientation, + ...props +}: StepProps) => { + const theme = stepTheme(); + + const active = currentIndex === index; + const completed = currentIndex > index; + + const renderConnector = useCallback( + (index: number) => { + if (index === 0) { + return null; + } + + return
; + }, + [theme], + ); + + return ( + <> + {renderConnector(index)} +
  • + { +
    + {completed ? ( + + ) : ( + index + 1 + )} +
    + } +

    {label}

    +
  • + + ); +}; diff --git a/packages/frontend/src/components/shared/Steps/Step/index.ts b/packages/frontend/src/components/shared/Steps/Step/index.ts new file mode 100644 index 00000000..84d83fcb --- /dev/null +++ b/packages/frontend/src/components/shared/Steps/Step/index.ts @@ -0,0 +1,2 @@ +export * from './Step'; +export * from './Step.theme'; diff --git a/packages/frontend/src/components/shared/Steps/Steps.theme.ts b/packages/frontend/src/components/shared/Steps/Steps.theme.ts new file mode 100644 index 00000000..a5d62e99 --- /dev/null +++ b/packages/frontend/src/components/shared/Steps/Steps.theme.ts @@ -0,0 +1,18 @@ +import { VariantProps, tv } from 'tailwind-variants'; + +export const stepsTheme = tv({ + slots: { + root: [], + }, + variants: { + orientation: { + vertical: { root: ['flex', 'flex-col'] }, + horizontal: { root: ['flex', 'items-center'] }, + }, + }, + defaultVariants: { + orientation: 'vertical', + }, +}); + +export type StepsTheme = VariantProps; diff --git a/packages/frontend/src/components/shared/Steps/Steps.tsx b/packages/frontend/src/components/shared/Steps/Steps.tsx new file mode 100644 index 00000000..51d4eb2d --- /dev/null +++ b/packages/frontend/src/components/shared/Steps/Steps.tsx @@ -0,0 +1,42 @@ +import React, { Fragment, ComponentPropsWithoutRef } from 'react'; +import { stepsTheme, StepsTheme } from './Steps.theme'; +import { Step, StepProps, StepTheme } from './Step'; + +interface StepsProps + extends ComponentPropsWithoutRef<'ul'>, + StepsTheme, + Pick { + /** + * The index of the current step + */ + currentIndex: number; + /** + * The steps to render + */ + steps: Pick[]; +} + +export const Steps = ({ + currentIndex, + steps = [], + className, + orientation, + ...props +}: StepsProps) => { + const theme = stepsTheme(); + + return ( +
      + {steps.map((step, i) => ( + + + + ))} +
    + ); +}; diff --git a/packages/frontend/src/components/shared/Steps/index.ts b/packages/frontend/src/components/shared/Steps/index.ts new file mode 100644 index 00000000..27d56a3b --- /dev/null +++ b/packages/frontend/src/components/shared/Steps/index.ts @@ -0,0 +1,2 @@ +export * from './Steps'; +export * from './Steps.theme'; From 8f0c39022dde1874a423b211c655e1952d108291 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 10:40:06 +0800 Subject: [PATCH 17/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20add=20render=20co?= =?UTF-8?q?mponent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/pages/components/index.tsx | 12 ++++++ .../src/pages/components/renders/steps.tsx | 41 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 packages/frontend/src/pages/components/renders/steps.tsx diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx index aab2d652..077434d5 100644 --- a/packages/frontend/src/pages/components/index.tsx +++ b/packages/frontend/src/pages/components/index.tsx @@ -26,6 +26,7 @@ import { import { renderInputs } from './renders/input'; import { RADIO_OPTIONS } from './renders/radio'; import { SEGMENTED_CONTROLS_OPTIONS } from './renders/segmentedControls'; +import { renderHorizontalSteps, renderVerticalSteps } from './renders/steps'; import { renderTabWithBadges, renderTabs, @@ -56,6 +57,17 @@ const Page: React.FC = () => {
    + {/* Steps */} +
    +
    +

    Steps

    +
    + {renderVerticalSteps()} + {renderHorizontalSteps()} +
    +
    +
    + {/* Tag */}
    diff --git a/packages/frontend/src/pages/components/renders/steps.tsx b/packages/frontend/src/pages/components/renders/steps.tsx new file mode 100644 index 00000000..2d3f3da5 --- /dev/null +++ b/packages/frontend/src/pages/components/renders/steps.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Steps } from 'components/shared/Steps'; + +export const renderVerticalSteps = () => { + return ( + + ); +}; + +export const renderHorizontalSteps = () => { + return ( + + ); +}; From f5296e9b8d00e586fa341ced47481fd061a412a1 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 11:52:31 +0800 Subject: [PATCH 18/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20first=20index=20t?= =?UTF-8?q?o=20be=20'1'=20for=20`Steps`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/shared/Steps/Step/Step.tsx | 4 ++-- packages/frontend/src/components/shared/Steps/Steps.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.tsx b/packages/frontend/src/components/shared/Steps/Step/Step.tsx index 001ed6b6..06c49368 100644 --- a/packages/frontend/src/components/shared/Steps/Step/Step.tsx +++ b/packages/frontend/src/components/shared/Steps/Step/Step.tsx @@ -31,7 +31,7 @@ export const Step = ({ const renderConnector = useCallback( (index: number) => { - if (index === 0) { + if (index === 1) { return null; } @@ -49,7 +49,7 @@ export const Step = ({ {completed ? ( ) : ( - index + 1 + index )}
    } diff --git a/packages/frontend/src/components/shared/Steps/Steps.tsx b/packages/frontend/src/components/shared/Steps/Steps.tsx index 51d4eb2d..cac61ff9 100644 --- a/packages/frontend/src/components/shared/Steps/Steps.tsx +++ b/packages/frontend/src/components/shared/Steps/Steps.tsx @@ -33,7 +33,7 @@ export const Steps = ({ {...step} orientation={orientation} currentIndex={currentIndex} - index={i} + index={i + 1} /> ))} From 460f8a80b8f12e0f91d0e043a0bf3b5dfb77c6d9 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 11:52:55 +0800 Subject: [PATCH 19/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20replace=20`Steppe?= =?UTF-8?q?r`=20with=20`Steps`=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/pages/org-slug/projects/create/Template.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 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 d819a6f1..0633540c 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/Template.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/Template.tsx @@ -8,8 +8,8 @@ import { import { Avatar } from '@material-tailwind/react'; -import Stepper from '../../../../components/Stepper'; import templates from '../../../../assets/templates'; +import { Steps } from 'components/shared/Steps'; // TODO: Set dynamic route for template and load details from DB const CreateWithTemplate = () => { @@ -62,7 +62,7 @@ const CreateWithTemplate = () => {
    - +
    From add2559860c34a7bffd1ffe7be0190cd693153a2 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 17:41:33 +0800 Subject: [PATCH 20/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20adjust=20other=20?= =?UTF-8?q?CheckRoundFilledIcon=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/projects/create/DeployStep.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/projects/create/DeployStep.tsx b/packages/frontend/src/components/projects/create/DeployStep.tsx index 71283ddb..de78f789 100644 --- a/packages/frontend/src/components/projects/create/DeployStep.tsx +++ b/packages/frontend/src/components/projects/create/DeployStep.tsx @@ -101,7 +101,12 @@ const DeployStep = ({ )} {status === DeployStatus.COMPLETE && (
    - +
    + +
    {' '}
    )} From c1da92bc991b085576936c0a7056e076a4bc4d51 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 17:50:04 +0800 Subject: [PATCH 21/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20adjust=20design?= =?UTF-8?q?=20to=20figma?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/shared/Steps/Step/Step.theme.ts | 10 +++++++--- .../src/components/shared/Steps/Step/Step.tsx | 13 ++++++++++--- .../src/components/shared/Steps/Steps.theme.ts | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts index 82531ace..30d190f8 100644 --- a/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts +++ b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts @@ -16,12 +16,16 @@ export const stepTheme = tv({ 'shrink-0', ], label: ['text-sm', 'font-sans', 'text-elements-mid-em'], - connector: ['bg-border-interactive-hovered'], + connector: [], }, variants: { orientation: { - vertical: { connector: ['w-px', 'h-3', 'ml-5'] }, - horizontal: { connector: ['h-px', 'w-full'] }, + vertical: { + connector: ['bg-border-interactive-hovered', 'w-px', 'h-3', 'ml-5'], + }, + horizontal: { + connector: ['text-border-interactive-hovered', 'h-3', 'w-3'], + }, }, active: { true: { diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.tsx b/packages/frontend/src/components/shared/Steps/Step/Step.tsx index 06c49368..9d731139 100644 --- a/packages/frontend/src/components/shared/Steps/Step/Step.tsx +++ b/packages/frontend/src/components/shared/Steps/Step/Step.tsx @@ -1,6 +1,9 @@ import React, { useCallback, ComponentPropsWithoutRef } from 'react'; import { stepTheme, StepTheme } from './Step.theme'; -import { CheckRoundFilledIcon } from 'components/shared/CustomIcon'; +import { + CheckRoundFilledIcon, + ChevronRight, +} from 'components/shared/CustomIcon'; export interface StepProps extends ComponentPropsWithoutRef<'li'>, StepTheme { /** @@ -35,9 +38,13 @@ export const Step = ({ return null; } - return
    ; + return ( +
    + {orientation === 'horizontal' && } +
    + ); }, - [theme], + [orientation, theme], ); return ( diff --git a/packages/frontend/src/components/shared/Steps/Steps.theme.ts b/packages/frontend/src/components/shared/Steps/Steps.theme.ts index a5d62e99..4c3b2acb 100644 --- a/packages/frontend/src/components/shared/Steps/Steps.theme.ts +++ b/packages/frontend/src/components/shared/Steps/Steps.theme.ts @@ -7,7 +7,7 @@ export const stepsTheme = tv({ variants: { orientation: { vertical: { root: ['flex', 'flex-col'] }, - horizontal: { root: ['flex', 'items-center'] }, + horizontal: { root: ['flex', 'items-center', 'gap-1'] }, }, }, defaultVariants: { From 0a2686d5ec94db3f7fe96351a08ecab0f69a47d7 Mon Sep 17 00:00:00 2001 From: Andre H Date: Mon, 4 Mar 2024 17:50:23 +0800 Subject: [PATCH 22/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20add=20divider=20t?= =?UTF-8?q?o=20render=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/components/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx index 077434d5..feee4595 100644 --- a/packages/frontend/src/pages/components/index.tsx +++ b/packages/frontend/src/pages/components/index.tsx @@ -68,6 +68,8 @@ const Page: React.FC = () => {
    +
    + {/* Tag */}
    From e8ae4177725d86156a15745966b765809ee32981 Mon Sep 17 00:00:00 2001 From: Andre H Date: Tue, 5 Mar 2024 09:39:00 +0800 Subject: [PATCH 23/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20whitespace=20nowr?= =?UTF-8?q?ap=20to=20prevent=20text=20warping=20on=20steps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/shared/Steps/Step/Step.theme.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts index 30d190f8..8e2b7a53 100644 --- a/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts +++ b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts @@ -15,7 +15,12 @@ export const stepTheme = tv({ 'shadow-button', 'shrink-0', ], - label: ['text-sm', 'font-sans', 'text-elements-mid-em'], + label: [ + 'text-sm', + 'font-sans', + 'text-elements-mid-em', + 'whitespace-nowrap', + ], connector: [], }, variants: { From f9ac778e47384f66ab4eec759545ac455143a296 Mon Sep 17 00:00:00 2001 From: Andre H Date: Tue, 5 Mar 2024 14:16:54 +0800 Subject: [PATCH 24/28] =?UTF-8?q?=E2=9A=92=EF=B8=8F=20build:=20add=20useho?= =?UTF-8?q?oks-ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index ec89a071..614068fb 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -49,7 +49,7 @@ "siwe": "^2.1.4", "tailwind-variants": "^0.2.0", "typescript": "^4.9.5", - "usehooks-ts": "^2.10.0", + "usehooks-ts": "^2.15.1", "vertical-stepper-nav": "^1.0.2", "viem": "^2.7.11", "wagmi": "^2.5.7", diff --git a/yarn.lock b/yarn.lock index e44f8793..2708695e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18209,7 +18209,7 @@ use-sync-external-store@1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== -usehooks-ts@^2.10.0: +usehooks-ts@^2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/usehooks-ts/-/usehooks-ts-2.15.1.tgz#ede348c6f01b4b4fe981e240551624885a2fed83" integrity sha512-AK29ODCt4FT9XleILNbkbjjmkRCNaQrgxQEkvqHjlnT76iPXzTFGvK2Y/s83JEdSxRp43YEnSa3bYBEV6HZ26Q== From c079a9c33674ecc9ee8fa105682e67175f03b0e1 Mon Sep 17 00:00:00 2001 From: Andre H Date: Tue, 5 Mar 2024 14:18:31 +0800 Subject: [PATCH 25/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20prevent=20Avatar?= =?UTF-8?q?=20from=20shrinking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/shared/Avatar/Avatar.theme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/shared/Avatar/Avatar.theme.ts b/packages/frontend/src/components/shared/Avatar/Avatar.theme.ts index b7522cfe..a72a1bc7 100644 --- a/packages/frontend/src/components/shared/Avatar/Avatar.theme.ts +++ b/packages/frontend/src/components/shared/Avatar/Avatar.theme.ts @@ -2,7 +2,7 @@ import { tv, type VariantProps } from 'tailwind-variants'; export const avatarTheme = tv( { - base: ['relative', 'block', 'rounded-full', 'overflow-hidden'], + base: ['relative', 'block', 'rounded-full', 'overflow-hidden', 'shrink-0'], slots: { image: [ 'h-full', From f6db1e119b4fdcaaedeb87ef3ebdb44c1dd7f513 Mon Sep 17 00:00:00 2001 From: Andre H Date: Tue, 5 Mar 2024 14:18:51 +0800 Subject: [PATCH 26/28] =?UTF-8?q?=F0=9F=94=A7=20chore:=20add=20overflow-sc?= =?UTF-8?q?roll=20to=20Tabs=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/shared/Tabs/Tabs.theme.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts b/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts index 81a32589..783d242b 100644 --- a/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts +++ b/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts @@ -71,6 +71,7 @@ export const tabsTheme = tv({ 'gap-5', 'border-b', 'border-transparent', + 'overflow-scroll', // Horizontal – default 'data-[orientation=horizontal]:border-border-interactive/10', // Vertical From 96f519d5e316e9edc22986a951318ed654d45280 Mon Sep 17 00:00:00 2001 From: Andre H Date: Tue, 5 Mar 2024 14:19:47 +0800 Subject: [PATCH 27/28] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20mobile=20lay?= =?UTF-8?q?out=20for=20project=20overview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/overview/Activity/Activity.tsx | 2 +- .../src/pages/org-slug/projects/Id.tsx | 21 ++++++++++++++----- .../pages/org-slug/projects/id/Overview.tsx | 12 +++++------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/components/projects/project/overview/Activity/Activity.tsx b/packages/frontend/src/components/projects/project/overview/Activity/Activity.tsx index 040683b9..252d85a1 100644 --- a/packages/frontend/src/components/projects/project/overview/Activity/Activity.tsx +++ b/packages/frontend/src/components/projects/project/overview/Activity/Activity.tsx @@ -11,7 +11,7 @@ export const Activity = ({ activities: GitCommitWithBranch[]; }) => { return ( -
    +
    Activity
    - - +
    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 81cdbe16..c1337045 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 28/28] =?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 668b3f8e..9db6dee1 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