From 4532a962d898adfeae521a62925419ce8a9878d3 Mon Sep 17 00:00:00 2001 From: Vivian Phung Date: Wed, 28 Feb 2024 23:44:10 -0700 Subject: [PATCH 01/11] fix navigate --- packages/frontend/src/assets/templates.ts | 4 ++-- .../projects/create/TemplateCard/TemplateCard.tsx | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/assets/templates.ts b/packages/frontend/src/assets/templates.ts index 84c60bb5..2d12165a 100644 --- a/packages/frontend/src/assets/templates.ts +++ b/packages/frontend/src/assets/templates.ts @@ -18,14 +18,14 @@ export default [ name: 'Kotlin', icon: 'kotlin', repoFullName: '', - isComingSoon: false, + isComingSoon: true, }, { id: '4', name: 'React Native', icon: 'react-native', repoFullName: '', - isComingSoon: false, + isComingSoon: true, }, { id: '5', diff --git a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx index b7c549fd..450760ba 100644 --- a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx +++ b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx @@ -1,3 +1,4 @@ +import React, { ComponentPropsWithoutRef, useCallback } from 'react'; import { Button } from 'components/shared/Button'; import { ArrowRightCircleIcon, @@ -6,7 +7,6 @@ import { TemplateIconType, } from 'components/shared/CustomIcon'; import { Tag } from 'components/shared/Tag'; -import React, { ComponentPropsWithoutRef, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { useToast } from 'components/shared/Toast'; import { cn } from 'utils/classnames'; @@ -24,7 +24,10 @@ export interface TemplateCardProps extends ComponentPropsWithoutRef<'div'> { isGitAuth: boolean; } -export const TemplateCard = ({ template, isGitAuth }: TemplateCardProps) => { +export const TemplateCard: React.FC = ({ + template, + isGitAuth, +}: TemplateCardProps) => { const { toast, dismiss } = useToast(); const navigate = useNavigate(); @@ -38,7 +41,7 @@ export const TemplateCard = ({ template, isGitAuth }: TemplateCardProps) => { }); } if (isGitAuth) { - return navigate(`/template?templateId=${template.id}`); + return navigate(`template?templateId=${template.id}`); } return toast({ id: 'connect-git-account', From 59467557498a5ae893a31f2b4f2d46edd20c3ba1 Mon Sep 17 00:00:00 2001 From: Vivian Phung Date: Wed, 28 Feb 2024 23:52:37 -0700 Subject: [PATCH 02/11] fix navigate --- .../projects/create/TemplateCard/TemplateCard.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx index 450760ba..dce0e20b 100644 --- a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx +++ b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx @@ -7,7 +7,7 @@ import { TemplateIconType, } from 'components/shared/CustomIcon'; import { Tag } from 'components/shared/Tag'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import { useToast } from 'components/shared/Toast'; import { cn } from 'utils/classnames'; @@ -30,6 +30,7 @@ export const TemplateCard: React.FC = ({ }: TemplateCardProps) => { const { toast, dismiss } = useToast(); const navigate = useNavigate(); + const { orgSlug } = useParams(); const handleClick = useCallback(() => { if (template?.isComingSoon) { @@ -41,7 +42,9 @@ export const TemplateCard: React.FC = ({ }); } if (isGitAuth) { - return navigate(`template?templateId=${template.id}`); + return navigate( + `/${orgSlug}/projects/create/template?templateId=${template.id}`, + ); } return toast({ id: 'connect-git-account', From dd5f93050d7282cb507cd4ad117b7fc9719fb0b7 Mon Sep 17 00:00:00 2001 From: Vivian Phung Date: Thu, 29 Feb 2024 00:01:56 -0700 Subject: [PATCH 03/11] fix navigate --- .../components/projects/create/TemplateCard/TemplateCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx index dce0e20b..ad073c2e 100644 --- a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx +++ b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx @@ -52,7 +52,7 @@ export const TemplateCard: React.FC = ({ variant: 'error', onDismiss: dismiss, }); - }, [isGitAuth, navigate, template, toast]); + }, [orgSlug, dismiss, isGitAuth, navigate, template, toast]); return ( - - - - - ^ 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 10/11] [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 11/11] [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';