forked from cerc-io/snowballtools-base
### TL;DR This PR updates the project settings. ### What changed? The project settings have been refactored for better organization and readability. ### How to test? To test this change, navigate to the project settings and ensure all options are functioning as expected. ### Why make this change? This change was made to improve the user experience when navigating through the project settings. ---
259 lines
8.1 KiB
TypeScript
259 lines
8.1 KiB
TypeScript
import { useState, ComponentPropsWithRef } from 'react';
|
|
|
|
import {
|
|
Menu,
|
|
MenuHandler,
|
|
MenuItem,
|
|
MenuList,
|
|
} from '@snowballtools/material-tailwind-react-fork';
|
|
|
|
import { Deployment, Domain, Environment, Project } from 'gql-client';
|
|
import { Button } from 'components/shared/Button';
|
|
import {
|
|
GlobeIcon,
|
|
HorizontalDotIcon,
|
|
LinkIcon,
|
|
RefreshIcon,
|
|
RocketIcon,
|
|
UndoIcon,
|
|
CrossCircleIcon,
|
|
} from 'components/shared/CustomIcon';
|
|
import AssignDomainDialog from './AssignDomainDialog';
|
|
import { useGQLClient } from 'context/GQLClientContext';
|
|
import { cn } from 'utils/classnames';
|
|
import { ChangeStateToProductionDialog } from 'components/projects/Dialog/ChangeStateToProductionDialog';
|
|
import { useToast } from 'components/shared/Toast';
|
|
|
|
interface DeploymentMenuProps extends ComponentPropsWithRef<'div'> {
|
|
deployment: Deployment;
|
|
currentDeployment: Deployment;
|
|
onUpdate: () => Promise<void>;
|
|
project: Project;
|
|
prodBranchDomains: Domain[];
|
|
}
|
|
|
|
export const DeploymentMenu = ({
|
|
deployment,
|
|
currentDeployment,
|
|
onUpdate,
|
|
project,
|
|
prodBranchDomains,
|
|
className,
|
|
...props
|
|
}: DeploymentMenuProps) => {
|
|
const client = useGQLClient();
|
|
const { toast, dismiss } = useToast();
|
|
|
|
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({
|
|
id: 'deployment_changed_to_production',
|
|
title: 'Deployment changed to production',
|
|
variant: 'success',
|
|
onDismiss: dismiss,
|
|
});
|
|
} else {
|
|
toast({
|
|
id: 'deployment_not_changed_to_production',
|
|
title: 'Error changing deployment to production',
|
|
variant: 'error',
|
|
onDismiss: dismiss,
|
|
});
|
|
}
|
|
};
|
|
|
|
const redeployToProd = async () => {
|
|
const isRedeployed = await client.redeployToProd(deployment.id);
|
|
if (isRedeployed) {
|
|
await onUpdate();
|
|
toast({
|
|
id: 'redeployed_to_production',
|
|
title: 'Redeployed to production',
|
|
variant: 'success',
|
|
onDismiss: dismiss,
|
|
});
|
|
} else {
|
|
toast({
|
|
id: 'redeployed_to_production_failed',
|
|
title: 'Error redeploying to production',
|
|
variant: 'error',
|
|
onDismiss: dismiss,
|
|
});
|
|
}
|
|
};
|
|
|
|
const rollbackDeploymentHandler = async () => {
|
|
const isRollbacked = await client.rollbackDeployment(
|
|
project.id,
|
|
deployment.id,
|
|
);
|
|
if (isRollbacked) {
|
|
await onUpdate();
|
|
toast({
|
|
id: 'deployment_rolled_back',
|
|
title: 'Deployment rolled back',
|
|
variant: 'success',
|
|
onDismiss: dismiss,
|
|
});
|
|
} else {
|
|
toast({
|
|
id: 'deployment_rollback_failed',
|
|
title: 'Error rolling back deployment',
|
|
variant: 'error',
|
|
onDismiss: dismiss,
|
|
});
|
|
}
|
|
};
|
|
|
|
const deleteDeployment = async () => {
|
|
const isDeleted = await client.deleteDeployment(deployment.id);
|
|
if (isDeleted) {
|
|
await onUpdate();
|
|
toast({
|
|
id: 'deployment_deleted',
|
|
title: 'Deployment deleted',
|
|
variant: 'success',
|
|
onDismiss: dismiss,
|
|
});
|
|
} else {
|
|
toast({
|
|
id: 'deployment_not_deleted',
|
|
title: 'Error deleting deployment',
|
|
variant: 'error',
|
|
onDismiss: dismiss,
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className={cn('max-w-[32px]', className)} {...props}>
|
|
<Menu placement="bottom-start">
|
|
<MenuHandler>
|
|
<Button
|
|
shape="default"
|
|
size="xs"
|
|
variant="unstyled"
|
|
className={cn(
|
|
'h-8 w-8 rounded-full border border-transparent transition-colors background-transparent',
|
|
'[&[aria-expanded=true]]:border [&[aria-expanded=true]]:border-border-interactive [&[aria-expanded=true]]:bg-controls-tertiary [&[aria-expanded=true]]:shadow-button',
|
|
)}
|
|
leftIcon={<HorizontalDotIcon />}
|
|
aria-label="Toggle Menu"
|
|
/>
|
|
</MenuHandler>
|
|
<MenuList className="text-elements-high-em">
|
|
<MenuItem
|
|
className="hover:bg-base-bg-emphasized"
|
|
disabled={!Boolean(deployment.url)}
|
|
>
|
|
<a
|
|
className="flex items-center gap-3"
|
|
href={deployment.url}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
>
|
|
<LinkIcon /> Visit
|
|
</a>
|
|
</MenuItem>
|
|
<MenuItem
|
|
className="hover:bg-base-bg-emphasized flex items-center gap-3"
|
|
onClick={() => setAssignDomainDialog(!assignDomainDialog)}
|
|
>
|
|
<GlobeIcon /> Assign domain
|
|
</MenuItem>
|
|
<MenuItem
|
|
className="hover:bg-base-bg-emphasized flex items-center gap-3"
|
|
onClick={() => setChangeToProduction(!changeToProduction)}
|
|
disabled={!(deployment.environment !== Environment.Production)}
|
|
>
|
|
<RocketIcon /> Change to production
|
|
</MenuItem>
|
|
<hr className="my-3" />
|
|
<MenuItem
|
|
className="hover:bg-base-bg-emphasized flex items-center gap-3"
|
|
onClick={() => setRedeployToProduction(!redeployToProduction)}
|
|
disabled={
|
|
!(
|
|
deployment.environment === Environment.Production &&
|
|
deployment.isCurrent
|
|
)
|
|
}
|
|
>
|
|
<RefreshIcon /> Redeploy to production
|
|
</MenuItem>
|
|
<MenuItem
|
|
className="hover:bg-base-bg-emphasized flex items-center gap-3"
|
|
onClick={() => setRollbackDeployment(!rollbackDeployment)}
|
|
disabled={
|
|
deployment.isCurrent ||
|
|
deployment.environment !== Environment.Production ||
|
|
!Boolean(currentDeployment)
|
|
}
|
|
>
|
|
<UndoIcon /> Rollback to this version
|
|
</MenuItem>
|
|
<MenuItem
|
|
className="hover:bg-base-bg-emphasized flex items-center gap-3"
|
|
onClick={() => deleteDeployment()}
|
|
>
|
|
<CrossCircleIcon /> Delete deployment
|
|
</MenuItem>
|
|
</MenuList>
|
|
</Menu>
|
|
</div>
|
|
{/* Dialogs */}
|
|
<ChangeStateToProductionDialog
|
|
dialogTitle="Change to production?"
|
|
confirmButtonTitle="Change"
|
|
handleCancel={() => setChangeToProduction((preVal) => !preVal)}
|
|
open={changeToProduction}
|
|
handleConfirm={async () => {
|
|
await updateDeployment();
|
|
setChangeToProduction((preVal) => !preVal);
|
|
}}
|
|
deployment={deployment}
|
|
domains={prodBranchDomains}
|
|
/>
|
|
<ChangeStateToProductionDialog
|
|
dialogTitle="Redeploy to production?"
|
|
handleCancel={() => setRedeployToProduction((preVal) => !preVal)}
|
|
open={redeployToProduction}
|
|
confirmButtonTitle="Redeploy"
|
|
handleConfirm={async () => {
|
|
await redeployToProd();
|
|
setRedeployToProduction((preVal) => !preVal);
|
|
}}
|
|
deployment={deployment}
|
|
domains={deployment.domain ? [deployment.domain] : []}
|
|
/>
|
|
{Boolean(currentDeployment) && (
|
|
<ChangeStateToProductionDialog
|
|
dialogTitle="Rollback to this deployment?"
|
|
handleCancel={() => setRollbackDeployment((preVal) => !preVal)}
|
|
open={rollbackDeployment}
|
|
confirmButtonTitle="Rollback"
|
|
handleConfirm={async () => {
|
|
await rollbackDeploymentHandler();
|
|
setRollbackDeployment((preVal) => !preVal);
|
|
}}
|
|
deployment={currentDeployment}
|
|
newDeployment={deployment}
|
|
domains={currentDeployment.domain ? [currentDeployment.domain] : []}
|
|
/>
|
|
)}
|
|
<AssignDomainDialog
|
|
open={assignDomainDialog}
|
|
handleOpen={() => setAssignDomainDialog(!assignDomainDialog)}
|
|
/>
|
|
</>
|
|
);
|
|
};
|