Add confirm dialogs for deployment menu (#24)

* Add dialogs for deployment menu items

* Populate deployment dialogs with dummy json

* Pass production deployment as prop to deployment card

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2023-12-26 11:53:28 +05:30 committed by GitHub
parent ef72a0351e
commit fc2bdefe17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 206 additions and 34 deletions

View File

@ -1,8 +1,9 @@
[ [
{ {
"id": 1,
"title": "nextjs-boilerplate-9t44zbky4dg-bygideon-projects", "title": "nextjs-boilerplate-9t44zbky4dg-bygideon-projects",
"status": "Building", "status": "Building",
"environment": "Production", "isProduction": true,
"branch": "prod", "branch": "prod",
"commit": { "commit": {
"hash": "9haif19", "hash": "9haif19",
@ -12,9 +13,10 @@
"updatedAt": "2023-12-11T04:20:00" "updatedAt": "2023-12-11T04:20:00"
}, },
{ {
"id": 2,
"title": "nextjs-boilerplate-9232dwky4dg-bygideon-projects", "title": "nextjs-boilerplate-9232dwky4dg-bygideon-projects",
"status": "Ready", "status": "Ready",
"environment": "Preview", "isProduction": false,
"branch": "prod", "branch": "prod",
"commit": { "commit": {
"hash": "43de569", "hash": "43de569",
@ -24,9 +26,10 @@
"updatedAt": "2023-12-11T04:20:00" "updatedAt": "2023-12-11T04:20:00"
}, },
{ {
"id": 3,
"title": "nextjs-boilerplate-9saa22y4dg-bygideon-projects", "title": "nextjs-boilerplate-9saa22y4dg-bygideon-projects",
"status": "Error", "status": "Error",
"environment": "Production", "isProduction": false,
"branch": "main", "branch": "main",
"commit": { "commit": {
"hash": "4hdsf19", "hash": "4hdsf19",

View File

@ -3,13 +3,12 @@ import React, { useCallback, useMemo, useState } from 'react';
import { Button, Typography } from '@material-tailwind/react'; import { Button, Typography } from '@material-tailwind/react';
import deploymentData from '../../../assets/deployments.json'; import deploymentData from '../../../assets/deployments.json';
import DeployDetailsCard, { import DeploymentDetailsCard from './deployments/DeploymentDetailsCard';
DeploymentDetails,
} from './deployments/DeploymentDetailsCard';
import FilterForm, { import FilterForm, {
FilterValue, FilterValue,
StatusOptions, StatusOptions,
} from './deployments/FilterForm'; } from './deployments/FilterForm';
import { DeploymentDetails } from '../../../types/project';
const DEFAULT_FILTER_VALUE: FilterValue = { const DEFAULT_FILTER_VALUE: FilterValue = {
searchedBranch: '', searchedBranch: '',
@ -19,6 +18,12 @@ const DEFAULT_FILTER_VALUE: FilterValue = {
const DeploymentsTabPanel = () => { const DeploymentsTabPanel = () => {
const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE); const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE);
const productionDeployment = useMemo(() => {
return deploymentData.find((deployment) => {
return deployment.isProduction === true;
}) as DeploymentDetails;
}, []);
const filteredDeployments = useMemo<DeploymentDetails[]>(() => { const filteredDeployments = useMemo<DeploymentDetails[]>(() => {
return deploymentData.filter((deployment) => { return deploymentData.filter((deployment) => {
// Searched branch filter // Searched branch filter
@ -55,7 +60,13 @@ const DeploymentsTabPanel = () => {
<div className="mt-2"> <div className="mt-2">
{Boolean(filteredDeployments.length) ? ( {Boolean(filteredDeployments.length) ? (
filteredDeployments.map((deployment, key) => { filteredDeployments.map((deployment, key) => {
return <DeployDetailsCard deployment={deployment} key={key} />; return (
<DeploymentDetailsCard
deployment={deployment}
key={key}
productionDeployment={productionDeployment}
/>
);
}) })
) : ( ) : (
<div className="h-[50vh] bg-gray-100 flex rounded items-center justify-center"> <div className="h-[50vh] bg-gray-100 flex rounded items-center justify-center">

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
Menu, Menu,
@ -11,28 +11,13 @@ import {
} from '@material-tailwind/react'; } from '@material-tailwind/react';
import { relativeTime } from '../../../../utils/time'; import { relativeTime } from '../../../../utils/time';
import ConfirmDialog from '../../../shared/ConfirmDialog';
export enum Status { import DeploymentDialogBodyCard from './DeploymentDialogBodyCard';
BUILDING = 'Building', import { DeploymentDetails, Status } from '../../../../types/project';
READY = 'Ready',
ERROR = 'Error',
}
export interface DeploymentDetails {
title: string;
status: Status;
environment: string;
branch: string;
commit: {
hash: string;
message: string;
};
author: string;
updatedAt: string;
}
interface DeployDetailsCardProps { interface DeployDetailsCardProps {
deployment: DeploymentDetails; deployment: DeploymentDetails;
productionDeployment: DeploymentDetails;
} }
const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = { const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = {
@ -41,7 +26,14 @@ const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = {
[Status.ERROR]: 'red', [Status.ERROR]: 'red',
}; };
const DeployDetailsCard = ({ deployment }: DeployDetailsCardProps) => { const DeploymentDetailsCard = ({
deployment,
productionDeployment,
}: DeployDetailsCardProps) => {
const [changeToProduction, setChangeToProduction] = useState(false);
const [redeployToProduction, setRedeployToProduction] = useState(false);
const [rollbackDeployment, setRollbackDeployment] = useState(false);
return ( return (
<div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2"> <div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
<div className="col-span-2"> <div className="col-span-2">
@ -54,7 +46,9 @@ const DeployDetailsCard = ({ deployment }: DeployDetailsCardProps) => {
icon={<i>^</i>} icon={<i>^</i>}
/> />
</div> </div>
<Typography color="gray">{deployment.environment}</Typography> <Typography color="gray">
{deployment.isProduction ? 'Production (Current)' : 'Preview'}
</Typography>
</div> </div>
<div className="col-span-1"> <div className="col-span-1">
<Typography color="gray">^ {deployment.branch}</Typography> <Typography color="gray">^ {deployment.branch}</Typography>
@ -73,15 +67,116 @@ const DeployDetailsCard = ({ deployment }: DeployDetailsCardProps) => {
<MenuList> <MenuList>
<MenuItem>^ Visit</MenuItem> <MenuItem>^ Visit</MenuItem>
<MenuItem>^ Assign domain</MenuItem> <MenuItem>^ Assign domain</MenuItem>
<MenuItem>^ Change to production</MenuItem> {!deployment.isProduction && (
<MenuItem
onClick={() => setChangeToProduction(!changeToProduction)}
>
^ Change to production
</MenuItem>
)}
<hr className="my-3" /> <hr className="my-3" />
<MenuItem>^ Redeploy</MenuItem> <MenuItem
<MenuItem>^ Rollback to this version</MenuItem> onClick={() => setRedeployToProduction(!redeployToProduction)}
>
^ Redeploy to production
</MenuItem>
{!deployment.isProduction && (
<MenuItem
onClick={() => setRollbackDeployment(!rollbackDeployment)}
>
^ Rollback to this version
</MenuItem>
)}
</MenuList> </MenuList>
</Menu> </Menu>
</div> </div>
<ConfirmDialog
dialogTitle="Change to production?"
handleOpen={() => setChangeToProduction(!changeToProduction)}
open={changeToProduction}
confirmButtonTitle="Change"
color="blue"
>
<div className="flex flex-col gap-2">
<Typography variant="small">
Upon confirmation, this deployment will be changed to production.
</Typography>
<DeploymentDialogBodyCard deployment={deployment} />
<Typography variant="small">
The new deployment will be associated with these domains:
</Typography>
<Typography variant="small" color="blue">
^ saugatt.com
</Typography>
<Typography variant="small" color="blue">
^ www.saugatt.com
</Typography>
</div>
</ConfirmDialog>
<ConfirmDialog
dialogTitle="Redeploy to production?"
handleOpen={() => setRedeployToProduction(!redeployToProduction)}
open={redeployToProduction}
confirmButtonTitle="Redeploy"
color="blue"
>
<div className="flex flex-col gap-2">
<Typography variant="small">
Upon confirmation, new deployment will be created with the same
source code as current deployment.
</Typography>
<DeploymentDialogBodyCard deployment={deployment} />
<Typography variant="small">
These domains will point to your new deployment:
</Typography>
<Typography variant="small" color="blue">
^ saugatt.com
</Typography>
<Typography variant="small" color="blue">
^ www.saugatt.com
</Typography>
</div>
</ConfirmDialog>
<ConfirmDialog
dialogTitle="Rollback to this deployment?"
handleOpen={() => setRollbackDeployment(!rollbackDeployment)}
open={rollbackDeployment}
confirmButtonTitle="Rollback"
color="blue"
>
<div className="flex flex-col gap-2">
<Typography variant="small">
Upon confirmation, this deployment will replace your current
deployment
</Typography>
<DeploymentDialogBodyCard
deployment={productionDeployment}
chip={{
value: 'Live Deployment',
color: 'green',
}}
/>
<DeploymentDialogBodyCard
deployment={deployment}
chip={{
value: 'New Deployment',
color: 'orange',
}}
/>
<Typography variant="small">
These domains will point to your new deployment:
</Typography>
<Typography variant="small" color="blue">
^ saugatt.com
</Typography>
<Typography variant="small" color="blue">
^ www.saugatt.com
</Typography>
</div>
</ConfirmDialog>
</div> </div>
); );
}; };
export default DeployDetailsCard; export default DeploymentDetailsCard;

View File

@ -0,0 +1,44 @@
import React from 'react';
import { Typography, Chip, Card } from '@material-tailwind/react';
import { color } from '@material-tailwind/react/types/components/chip';
import { DeploymentDetails } from '../../../../types/project';
import { relativeTime } from '../../../../utils/time';
interface DeploymentDialogBodyCardProps {
deployment: DeploymentDetails;
chip?: {
value: string;
color?: color;
};
}
const DeploymentDialogBodyCard = ({
chip,
deployment,
}: DeploymentDialogBodyCardProps) => {
return (
<Card className="p-2 shadow-none">
{chip && (
<Chip
className={`w-fit normal-case font-normal`}
size="sm"
value={chip.value}
color={chip.color}
/>
)}
<Typography variant="small" className="text-black">
deployment title
</Typography>
<Typography variant="small">
^ {deployment.branch} ^ {deployment.commit.hash}{' '}
{deployment.commit.message}
</Typography>
<Typography variant="small">
^ {relativeTime(deployment.updatedAt)} ^ {deployment.author}
</Typography>
</Card>
);
};
export default DeploymentDialogBodyCard;

View File

@ -16,7 +16,7 @@ type ConfirmDialogProp = {
open: boolean; open: boolean;
handleOpen: () => void; handleOpen: () => void;
confirmButtonTitle: string; confirmButtonTitle: string;
handleConfirm: () => void; handleConfirm?: () => void;
color: color; color: color;
}; };

View File

@ -16,3 +16,22 @@ export interface ProjectDetails {
branch: string; branch: string;
}; };
} }
export interface DeploymentDetails {
title: string;
isProduction: boolean;
status: Status;
branch: string;
commit: {
hash: string;
message: string;
};
author: string;
updatedAt: string;
}
export enum Status {
BUILDING = 'Building',
READY = 'Ready',
ERROR = 'Error',
}