forked from cerc-io/snowballtools-base
Merge pull request #162 from snowball-tools/ayungavis/T-4943-product-reskin-confirmdialog-styling
This commit is contained in:
commit
78f04c3669
@ -0,0 +1,30 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface CancelDeploymentDialogProps extends ConfirmDialogProps {}
|
||||||
|
|
||||||
|
export const CancelDeploymentDialog = ({
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: CancelDeploymentDialogProps) => {
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
dialogTitle="Cancel deployment?"
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
confirmButtonTitle="Yes, cancel deployment"
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
confirmButtonProps={{ variant: 'danger' }}
|
||||||
|
>
|
||||||
|
<p className="text-sm text-elements-high-em tracking-[-0.006em]">
|
||||||
|
This will halt the deployment and you'll have to start the process
|
||||||
|
from scratch.
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,90 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import { Deployment, Domain } from 'gql-client';
|
||||||
|
import React from 'react';
|
||||||
|
import DeploymentDialogBodyCard from 'components/projects/project/deployments/DeploymentDialogBodyCard';
|
||||||
|
import { Button } from 'components/shared/Button';
|
||||||
|
import {
|
||||||
|
ChevronDoubleDownIcon,
|
||||||
|
LinkChainIcon,
|
||||||
|
} from 'components/shared/CustomIcon';
|
||||||
|
import { TagProps } from 'components/shared/Tag';
|
||||||
|
|
||||||
|
interface ChangeStateToProductionDialogProps extends ConfirmDialogProps {
|
||||||
|
deployment: Deployment;
|
||||||
|
newDeployment?: Deployment;
|
||||||
|
domains: Domain[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChangeStateToProductionDialog = ({
|
||||||
|
deployment,
|
||||||
|
newDeployment,
|
||||||
|
domains,
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: ChangeStateToProductionDialogProps) => {
|
||||||
|
const currentChip = {
|
||||||
|
value: 'Live Deployment',
|
||||||
|
type: 'positive' as TagProps['type'],
|
||||||
|
};
|
||||||
|
const newChip = {
|
||||||
|
value: 'New Deployment',
|
||||||
|
type: 'attention' as TagProps['type'],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-7">
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<p className="text-sm text-elements-high-em tracking-[-0.006em]">
|
||||||
|
Upon confirmation, this deployment will be changed to production.
|
||||||
|
</p>
|
||||||
|
<DeploymentDialogBodyCard
|
||||||
|
deployment={deployment}
|
||||||
|
chip={newDeployment ? currentChip : undefined}
|
||||||
|
/>
|
||||||
|
{newDeployment && (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center justify-between w-full text-elements-info">
|
||||||
|
{Array.from({ length: 7 }).map((_, index) => (
|
||||||
|
<ChevronDoubleDownIcon key={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<DeploymentDialogBodyCard
|
||||||
|
deployment={newDeployment}
|
||||||
|
chip={newChip}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-start gap-3">
|
||||||
|
<p className="text-sm text-elements-high-em tracking-[-0.006em]">
|
||||||
|
The new deployment will be associated with these domains:
|
||||||
|
</p>
|
||||||
|
{domains.length > 0 &&
|
||||||
|
domains.map((value) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
as="a"
|
||||||
|
href={value.name}
|
||||||
|
leftIcon={<LinkChainIcon size={18} />}
|
||||||
|
variant="link"
|
||||||
|
key={value.id}
|
||||||
|
>
|
||||||
|
{value.name}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,42 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface DeleteDomainDialogProps extends ConfirmDialogProps {
|
||||||
|
projectName: string;
|
||||||
|
domainName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DeleteDomainDialog = ({
|
||||||
|
projectName,
|
||||||
|
domainName,
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: DeleteDomainDialogProps) => {
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
dialogTitle="Delete domain?"
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
confirmButtonTitle="Yes, delete domain"
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
confirmButtonProps={{ variant: 'danger' }}
|
||||||
|
>
|
||||||
|
<p className="text-sm text-elements-high-em">
|
||||||
|
Once deleted, the project{' '}
|
||||||
|
<span className="text-sm font-mono text-elements-on-secondary bg-controls-secondary rounded px-0.5">
|
||||||
|
{projectName}
|
||||||
|
</span>{' '}
|
||||||
|
will not be accessible from the domain{' '}
|
||||||
|
<span className="text-sm font-mono text-elements-on-secondary bg-controls-secondary rounded px-0.5">
|
||||||
|
{domainName}
|
||||||
|
</span>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,36 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface DeleteVariableDialogProps extends ConfirmDialogProps {
|
||||||
|
variableKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DeleteVariableDialog = ({
|
||||||
|
variableKey,
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: DeleteVariableDialogProps) => {
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
dialogTitle="Delete variable"
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
confirmButtonTitle="Yes, confirm delete"
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
confirmButtonProps={{ variant: 'danger' }}
|
||||||
|
>
|
||||||
|
<p className="text-sm text-elements-mid-em">
|
||||||
|
Are you sure you want to delete the variable{' '}
|
||||||
|
<span className="text-sm font-mono text-elements-on-secondary bg-controls-secondary rounded px-0.5">
|
||||||
|
{variableKey}
|
||||||
|
</span>
|
||||||
|
?
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,36 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface DeleteWebhookDialogProps extends ConfirmDialogProps {
|
||||||
|
webhookUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DeleteWebhookDialog = ({
|
||||||
|
webhookUrl,
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: DeleteWebhookDialogProps) => {
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
dialogTitle="Delete webhook?"
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
confirmButtonTitle="Yes, confirm delete"
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
confirmButtonProps={{ variant: 'danger' }}
|
||||||
|
>
|
||||||
|
<p className="text-sm text-elements-mid-em">
|
||||||
|
Are you sure you want to delete{' '}
|
||||||
|
<span className="text-sm font-mono text-elements-high-em px-0.5">
|
||||||
|
{webhookUrl}
|
||||||
|
</span>
|
||||||
|
?
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,30 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface DisconnectRepositoryDialogProps extends ConfirmDialogProps {}
|
||||||
|
|
||||||
|
export const DisconnectRepositoryDialog = ({
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: DisconnectRepositoryDialogProps) => {
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
dialogTitle="Disconnect repository?"
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
confirmButtonTitle="Yes, confirm disconnect"
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
confirmButtonProps={{ variant: 'danger' }}
|
||||||
|
>
|
||||||
|
<p className="text-sm text-elements-high-em">
|
||||||
|
Any data tied to your Git project may become misconfigured. Are you sure
|
||||||
|
you want to continue?
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,38 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import React from 'react';
|
||||||
|
import { formatAddress } from 'utils/format';
|
||||||
|
|
||||||
|
interface RemoveMemberDialogProps extends ConfirmDialogProps {
|
||||||
|
memberName: string;
|
||||||
|
ethAddress: string;
|
||||||
|
emailDomain: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RemoveMemberDialog = ({
|
||||||
|
memberName,
|
||||||
|
ethAddress,
|
||||||
|
emailDomain,
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: RemoveMemberDialogProps) => {
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
dialogTitle="Remove member?"
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
confirmButtonTitle="Yes, remove member"
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
confirmButtonProps={{ variant: 'danger' }}
|
||||||
|
>
|
||||||
|
<p className="text-sm text-elements-high-em">
|
||||||
|
Once removed, {formatAddress(memberName)} ({formatAddress(ethAddress)}@
|
||||||
|
{emailDomain}) will not be able to access this project.
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,47 @@
|
|||||||
|
import ConfirmDialog, {
|
||||||
|
ConfirmDialogProps,
|
||||||
|
} from 'components/shared/ConfirmDialog';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface TransferProjectDialogProps extends ConfirmDialogProps {
|
||||||
|
projectName: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TransferProjectDialog = ({
|
||||||
|
projectName,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
...props
|
||||||
|
}: TransferProjectDialogProps) => {
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
{...props}
|
||||||
|
dialogTitle="Transfer project?"
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
open={open}
|
||||||
|
confirmButtonTitle="Yes, confirm transfer"
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
>
|
||||||
|
<p className="text-sm text-elements-high-em">
|
||||||
|
Upon confirmation, your project{' '}
|
||||||
|
<span className="text-sm font-mono text-elements-on-secondary bg-controls-secondary rounded px-0.5">
|
||||||
|
{projectName}
|
||||||
|
</span>{' '}
|
||||||
|
will be transferred from{' '}
|
||||||
|
<span className="text-sm font-mono text-elements-on-secondary bg-controls-secondary rounded px-0.5">
|
||||||
|
{from}
|
||||||
|
</span>{' '}
|
||||||
|
to{' '}
|
||||||
|
<span className="text-sm font-mono text-elements-on-secondary bg-controls-secondary rounded px-0.5">
|
||||||
|
{to}
|
||||||
|
</span>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -12,6 +12,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { useCombobox } from 'downshift';
|
import { useCombobox } from 'downshift';
|
||||||
|
|
||||||
interface ProjectSearchBarDialogProps extends Dialog.DialogProps {
|
interface ProjectSearchBarDialogProps extends Dialog.DialogProps {
|
||||||
|
open?: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
onClickItem?: (data: Project) => void;
|
onClickItem?: (data: Project) => void;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Typography } from '@material-tailwind/react';
|
|
||||||
|
|
||||||
import { DeployStep, DeployStatus } from './DeployStep';
|
import { DeployStep, DeployStatus } from './DeployStep';
|
||||||
import { Stopwatch, setStopWatchOffset } from '../../StopWatch';
|
import { Stopwatch, setStopWatchOffset } from 'components/StopWatch';
|
||||||
import ConfirmDialog from 'components/shared/ConfirmDialog';
|
|
||||||
import { Heading } from 'components/shared/Heading';
|
import { Heading } from 'components/shared/Heading';
|
||||||
import { Button } from 'components/shared/Button';
|
import { Button } from 'components/shared/Button';
|
||||||
import { ClockOutlineIcon, WarningIcon } from 'components/shared/CustomIcon';
|
import { ClockOutlineIcon, WarningIcon } from 'components/shared/CustomIcon';
|
||||||
|
import { CancelDeploymentDialog } from 'components/projects/Dialog/CancelDeploymentDialog';
|
||||||
|
|
||||||
const TIMEOUT_DURATION = 5000;
|
const TIMEOUT_DURATION = 5000;
|
||||||
const Deploy = () => {
|
const Deploy = () => {
|
||||||
@ -55,19 +53,11 @@ const Deploy = () => {
|
|||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<ConfirmDialog
|
<CancelDeploymentDialog
|
||||||
dialogTitle="Cancel deployment?"
|
handleCancel={handleOpen}
|
||||||
handleOpen={handleOpen}
|
|
||||||
open={open}
|
open={open}
|
||||||
confirmButtonTitle="Yes, Cancel deployment"
|
|
||||||
handleConfirm={handleCancel}
|
handleConfirm={handleCancel}
|
||||||
color="red"
|
/>
|
||||||
>
|
|
||||||
<Typography variant="small" placeholder={''}>
|
|
||||||
This will halt the deployment and you will have to start the process
|
|
||||||
from scratch.
|
|
||||||
</Typography>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Deployment } from 'gql-client';
|
import { Deployment } from 'gql-client';
|
||||||
|
|
||||||
import { Typography, Chip, Card } from '@material-tailwind/react';
|
import { relativeTimeMs } from 'utils/time';
|
||||||
import { color } from '@material-tailwind/react/types/components/chip';
|
|
||||||
import { relativeTimeMs } from '../../../../utils/time';
|
|
||||||
import { SHORT_COMMIT_HASH_LENGTH } from '../../../../constants';
|
import { SHORT_COMMIT_HASH_LENGTH } from '../../../../constants';
|
||||||
import { formatAddress } from '../../../../utils/format';
|
import {
|
||||||
|
BranchStrokeIcon,
|
||||||
|
ClockOutlineIcon,
|
||||||
|
CommitIcon,
|
||||||
|
} from 'components/shared/CustomIcon';
|
||||||
|
import { Avatar } from 'components/shared/Avatar';
|
||||||
|
import { getInitials } from 'utils/geInitials';
|
||||||
|
import { OverflownText } from 'components/shared/OverflownText';
|
||||||
|
import { Tag, TagProps } from 'components/shared/Tag';
|
||||||
|
|
||||||
interface DeploymentDialogBodyCardProps {
|
interface DeploymentDialogBodyCardProps {
|
||||||
deployment: Deployment;
|
deployment: Deployment;
|
||||||
chip?: {
|
chip?: {
|
||||||
value: string;
|
value: string;
|
||||||
color?: color;
|
type?: TagProps['type'];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,31 +25,54 @@ const DeploymentDialogBodyCard = ({
|
|||||||
chip,
|
chip,
|
||||||
deployment,
|
deployment,
|
||||||
}: DeploymentDialogBodyCardProps) => {
|
}: DeploymentDialogBodyCardProps) => {
|
||||||
|
const commit =
|
||||||
|
deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH) +
|
||||||
|
' ' +
|
||||||
|
deployment.commitMessage;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="p-2 shadow-none" placeholder={''}>
|
<div className="flex flex-col gap-4 px-4 py-4 rounded-xl bg-base-bg-emphasized text-elements-low-em">
|
||||||
{chip && (
|
{chip && (
|
||||||
<Chip
|
<Tag className="w-fit" size="xs" type={chip.type}>
|
||||||
className={`w-fit normal-case font-normal`}
|
{chip.value}
|
||||||
size="sm"
|
</Tag>
|
||||||
value={chip.value}
|
|
||||||
color={chip.color}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{deployment.url && (
|
<div className="flex flex-col gap-3">
|
||||||
<Typography variant="small" className="text-black" placeholder={''}>
|
{/* Title */}
|
||||||
|
<p className="text-sm font-medium text-elements-high-em tracking-[0.006em]">
|
||||||
{deployment.url}
|
{deployment.url}
|
||||||
</Typography>
|
</p>
|
||||||
)}
|
{/* Branch & commit */}
|
||||||
<Typography variant="small" placeholder={''}>
|
<div className="flex items-center gap-6 text-elements-low-em">
|
||||||
^ {deployment.branch} ^{' '}
|
<div className="flex items-center gap-2">
|
||||||
{deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '}
|
<BranchStrokeIcon size={16} />
|
||||||
{deployment.commitMessage}
|
<p className="text-sm tracking-[0.006em]">{deployment.branch}</p>
|
||||||
</Typography>
|
</div>
|
||||||
<Typography variant="small" placeholder={''}>
|
<div className="flex items-center gap-2 w-full">
|
||||||
^ {relativeTimeMs(deployment.createdAt)} ^{' '}
|
<CommitIcon size={16} />
|
||||||
{formatAddress(deployment.createdBy.name ?? '')}
|
<p className="text-sm tracking-[0.006em] max-w-[67.5%] sm:max-w-[80%]">
|
||||||
</Typography>
|
<OverflownText content={commit}>{commit}</OverflownText>
|
||||||
</Card>
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-elements-low-em">
|
||||||
|
<ClockOutlineIcon size={16} />
|
||||||
|
<p className="text-sm tracking-[0.006em]">
|
||||||
|
{relativeTimeMs(deployment.createdAt)}
|
||||||
|
</p>
|
||||||
|
<Avatar
|
||||||
|
size={20}
|
||||||
|
type="orange"
|
||||||
|
initials={getInitials(deployment.createdBy.name ?? '')}
|
||||||
|
// TODO: Add avatar image URL
|
||||||
|
// imageSrc={deployment.createdBy.imageUrl}
|
||||||
|
/>
|
||||||
|
<p className="text-sm tracking-[0.006em]">
|
||||||
|
{deployment.createdBy.name ?? 'Unknown'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,12 +17,10 @@ import {
|
|||||||
MenuList,
|
MenuList,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
import { ComponentPropsWithRef } from 'react';
|
import { ComponentPropsWithRef } from 'react';
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
|
||||||
import AssignDomainDialog from './AssignDomainDialog';
|
import AssignDomainDialog from './AssignDomainDialog';
|
||||||
import DeploymentDialogBodyCard from './DeploymentDialogBodyCard';
|
import { useGQLClient } from 'context/GQLClientContext';
|
||||||
import { Typography } from '@material-tailwind/react';
|
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
|
||||||
import { cn } from 'utils/classnames';
|
import { cn } from 'utils/classnames';
|
||||||
|
import { ChangeStateToProductionDialog } from 'components/projects/Dialog/ChangeStateToProductionDialog';
|
||||||
|
|
||||||
interface DeploymentMenuProps extends ComponentPropsWithRef<'div'> {
|
interface DeploymentMenuProps extends ComponentPropsWithRef<'div'> {
|
||||||
deployment: Deployment;
|
deployment: Deployment;
|
||||||
@ -158,106 +156,44 @@ export const DeploymentMenu = ({
|
|||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
{/* Dialogs */}
|
{/* Dialogs */}
|
||||||
<ConfirmDialog
|
<ChangeStateToProductionDialog
|
||||||
dialogTitle="Change to production?"
|
dialogTitle="Change to production?"
|
||||||
handleOpen={() => setChangeToProduction((preVal) => !preVal)}
|
|
||||||
open={changeToProduction}
|
|
||||||
confirmButtonTitle="Change"
|
confirmButtonTitle="Change"
|
||||||
color="blue"
|
handleCancel={() => setChangeToProduction((preVal) => !preVal)}
|
||||||
|
open={changeToProduction}
|
||||||
handleConfirm={async () => {
|
handleConfirm={async () => {
|
||||||
await updateDeployment();
|
await updateDeployment();
|
||||||
setChangeToProduction((preVal) => !preVal);
|
setChangeToProduction((preVal) => !preVal);
|
||||||
}}
|
}}
|
||||||
>
|
deployment={deployment}
|
||||||
<div className="flex flex-col gap-2">
|
domains={prodBranchDomains}
|
||||||
<Typography variant="small" placeholder={''}>
|
/>
|
||||||
Upon confirmation, this deployment will be changed to production.
|
<ChangeStateToProductionDialog
|
||||||
</Typography>
|
|
||||||
<DeploymentDialogBodyCard deployment={deployment} />
|
|
||||||
<Typography variant="small" placeholder={''}>
|
|
||||||
The new deployment will be associated with these domains:
|
|
||||||
</Typography>
|
|
||||||
{prodBranchDomains.length > 0 &&
|
|
||||||
prodBranchDomains.map((value) => {
|
|
||||||
return (
|
|
||||||
<Typography
|
|
||||||
variant="small"
|
|
||||||
color="blue"
|
|
||||||
key={value.id}
|
|
||||||
placeholder={''}
|
|
||||||
>
|
|
||||||
^ {value.name}
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</ConfirmDialog>
|
|
||||||
<ConfirmDialog
|
|
||||||
dialogTitle="Redeploy to production?"
|
dialogTitle="Redeploy to production?"
|
||||||
handleOpen={() => setRedeployToProduction((preVal) => !preVal)}
|
handleCancel={() => setRedeployToProduction((preVal) => !preVal)}
|
||||||
open={redeployToProduction}
|
open={redeployToProduction}
|
||||||
confirmButtonTitle="Redeploy"
|
confirmButtonTitle="Redeploy"
|
||||||
color="blue"
|
|
||||||
handleConfirm={async () => {
|
handleConfirm={async () => {
|
||||||
await redeployToProd();
|
await redeployToProd();
|
||||||
setRedeployToProduction((preVal) => !preVal);
|
setRedeployToProduction((preVal) => !preVal);
|
||||||
}}
|
}}
|
||||||
>
|
deployment={deployment}
|
||||||
<div className="flex flex-col gap-2">
|
domains={deployment.domain ? [deployment.domain] : []}
|
||||||
<Typography variant="small" placeholder={''}>
|
/>
|
||||||
Upon confirmation, new deployment will be created with the same
|
|
||||||
source code as current deployment.
|
|
||||||
</Typography>
|
|
||||||
<DeploymentDialogBodyCard deployment={deployment} />
|
|
||||||
<Typography variant="small" placeholder={''}>
|
|
||||||
These domains will point to your new deployment:
|
|
||||||
</Typography>
|
|
||||||
{deployment.domain?.name && (
|
|
||||||
<Typography variant="small" color="blue" placeholder={''}>
|
|
||||||
{deployment.domain?.name}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ConfirmDialog>
|
|
||||||
{Boolean(currentDeployment) && (
|
{Boolean(currentDeployment) && (
|
||||||
<ConfirmDialog
|
<ChangeStateToProductionDialog
|
||||||
dialogTitle="Rollback to this deployment?"
|
dialogTitle="Rollback to this deployment?"
|
||||||
handleOpen={() => setRollbackDeployment((preVal) => !preVal)}
|
handleCancel={() => setRollbackDeployment((preVal) => !preVal)}
|
||||||
open={rollbackDeployment}
|
open={rollbackDeployment}
|
||||||
confirmButtonTitle="Rollback"
|
confirmButtonTitle="Rollback"
|
||||||
color="blue"
|
|
||||||
handleConfirm={async () => {
|
handleConfirm={async () => {
|
||||||
await rollbackDeploymentHandler();
|
await rollbackDeploymentHandler();
|
||||||
setRollbackDeployment((preVal) => !preVal);
|
setRollbackDeployment((preVal) => !preVal);
|
||||||
}}
|
}}
|
||||||
>
|
deployment={currentDeployment}
|
||||||
<div className="flex flex-col gap-2">
|
newDeployment={deployment}
|
||||||
<Typography variant="small" placeholder={''}>
|
domains={currentDeployment.domain ? [currentDeployment.domain] : []}
|
||||||
Upon confirmation, this deployment will replace your current
|
/>
|
||||||
deployment
|
|
||||||
</Typography>
|
|
||||||
<DeploymentDialogBodyCard
|
|
||||||
deployment={currentDeployment}
|
|
||||||
chip={{
|
|
||||||
value: 'Live Deployment',
|
|
||||||
color: 'green',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<DeploymentDialogBodyCard
|
|
||||||
deployment={deployment}
|
|
||||||
chip={{
|
|
||||||
value: 'New Deployment',
|
|
||||||
color: 'orange',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography variant="small" placeholder={''}>
|
|
||||||
These domains will point to your new deployment:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="small" color="blue" placeholder={''}>
|
|
||||||
^ {currentDeployment.domain?.name}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
</ConfirmDialog>
|
|
||||||
)}
|
)}
|
||||||
<AssignDomainDialog
|
<AssignDomainDialog
|
||||||
open={assignDomainDialog}
|
open={assignDomainDialog}
|
||||||
|
@ -12,9 +12,9 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
|
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
|
||||||
import EditDomainDialog from './EditDomainDialog';
|
import EditDomainDialog from './EditDomainDialog';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from 'context/GQLClientContext';
|
||||||
|
import { DeleteDomainDialog } from 'components/projects/Dialog/DeleteDomainDialog';
|
||||||
|
|
||||||
enum RefreshStatus {
|
enum RefreshStatus {
|
||||||
IDLE,
|
IDLE,
|
||||||
@ -118,28 +118,16 @@ const DomainCard = ({
|
|||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ConfirmDialog
|
<DeleteDomainDialog
|
||||||
dialogTitle="Delete domain?"
|
handleCancel={() => setDeleteDialogOpen((preVal) => !preVal)}
|
||||||
handleOpen={() => setDeleteDialogOpen((preVal) => !preVal)}
|
|
||||||
open={deleteDialogOpen}
|
open={deleteDialogOpen}
|
||||||
confirmButtonTitle="Yes, Delete domain"
|
|
||||||
handleConfirm={() => {
|
handleConfirm={() => {
|
||||||
deleteDomain();
|
deleteDomain();
|
||||||
setDeleteDialogOpen((preVal) => !preVal);
|
setDeleteDialogOpen((preVal) => !preVal);
|
||||||
}}
|
}}
|
||||||
color="red"
|
projectName={project.name}
|
||||||
>
|
domainName={domain.name}
|
||||||
<Typography variant="small" placeholder={''}>
|
/>
|
||||||
Once deleted, the project{' '}
|
|
||||||
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
|
|
||||||
{project.name}
|
|
||||||
</span>{' '}
|
|
||||||
will not be accessible from the domain{' '}
|
|
||||||
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
|
|
||||||
{domain.name}.
|
|
||||||
</span>
|
|
||||||
</Typography>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Typography variant="small" placeholder={''}>
|
<Typography variant="small" placeholder={''}>
|
||||||
|
@ -5,8 +5,8 @@ import { EnvironmentVariable } from 'gql-client';
|
|||||||
|
|
||||||
import { IconButton, Input, Typography } from '@material-tailwind/react';
|
import { IconButton, Input, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import { useGQLClient } from 'context/GQLClientContext';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { DeleteVariableDialog } from 'components/projects/Dialog/DeleteVariableDialog';
|
||||||
|
|
||||||
const ShowPasswordIcon = ({
|
const ShowPasswordIcon = ({
|
||||||
handler,
|
handler,
|
||||||
@ -161,20 +161,12 @@ const EditEnvironmentVariableRow = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<DeleteVariableDialog
|
||||||
<ConfirmDialog
|
handleCancel={() => setDeleteDialogOpen((preVal) => !preVal)}
|
||||||
dialogTitle="Delete variable"
|
|
||||||
handleOpen={() => setDeleteDialogOpen((preVal) => !preVal)}
|
|
||||||
open={deleteDialogOpen}
|
open={deleteDialogOpen}
|
||||||
confirmButtonTitle="Yes, Confirm delete"
|
|
||||||
handleConfirm={removeEnvironmentVariableHandler}
|
handleConfirm={removeEnvironmentVariableHandler}
|
||||||
color="red"
|
variableKey={variable.key}
|
||||||
>
|
/>
|
||||||
<Typography variant="small" placeholder={''}>
|
|
||||||
Are you sure you want to delete the variable
|
|
||||||
<span className="bg-blue-100">{variable.key}</span>?
|
|
||||||
</Typography>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,15 +3,14 @@ import { Permission, User } from 'gql-client';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
Typography,
|
|
||||||
Option,
|
Option,
|
||||||
Chip,
|
Chip,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
|
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import { formatAddress } from 'utils/format';
|
||||||
import { formatAddress } from '../../../../utils/format';
|
import { RemoveMemberDialog } from 'components/projects/Dialog/RemoveMemberDialog';
|
||||||
|
|
||||||
const PERMISSION_OPTIONS = [
|
const PERMISSION_OPTIONS = [
|
||||||
{
|
{
|
||||||
@ -141,25 +140,19 @@ const MemberCard = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ConfirmDialog
|
<RemoveMemberDialog
|
||||||
dialogTitle="Remove member?"
|
handleCancel={() => setRemoveMemberDialogOpen((preVal) => !preVal)}
|
||||||
handleOpen={() => setRemoveMemberDialogOpen((preVal) => !preVal)}
|
|
||||||
open={removeMemberDialogOpen}
|
open={removeMemberDialogOpen}
|
||||||
confirmButtonTitle="Yes, Remove member"
|
|
||||||
handleConfirm={() => {
|
handleConfirm={() => {
|
||||||
setRemoveMemberDialogOpen((preVal) => !preVal);
|
setRemoveMemberDialogOpen((preVal) => !preVal);
|
||||||
if (onRemoveProjectMember) {
|
if (onRemoveProjectMember) {
|
||||||
onRemoveProjectMember();
|
onRemoveProjectMember();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
color="red"
|
memberName={member.name ?? ''}
|
||||||
>
|
ethAddress={ethAddress}
|
||||||
<Typography variant="small" placeholder={''}>
|
emailDomain={emailDomain}
|
||||||
Once removed, {formatAddress(member.name ?? '')} (
|
/>
|
||||||
{formatAddress(ethAddress)}@{emailDomain}) will not be able to access
|
|
||||||
this project.
|
|
||||||
</Typography>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,8 +2,8 @@ import React, { useState } from 'react';
|
|||||||
|
|
||||||
import { Button, Typography } from '@material-tailwind/react';
|
import { Button, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
import { GitRepositoryDetails } from '../../../../types';
|
import { GitRepositoryDetails } from 'types';
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import { DisconnectRepositoryDialog } from 'components/projects/Dialog/DisconnectRepositoryDialog';
|
||||||
|
|
||||||
const RepoConnectedSection = ({
|
const RepoConnectedSection = ({
|
||||||
linkedRepo,
|
linkedRepo,
|
||||||
@ -34,21 +34,13 @@ const RepoConnectedSection = ({
|
|||||||
^ Disconnect
|
^ Disconnect
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmDialog
|
<DisconnectRepositoryDialog
|
||||||
dialogTitle="Disconnect repository?"
|
handleCancel={() => setDisconnectRepoDialogOpen((preVal) => !preVal)}
|
||||||
handleOpen={() => setDisconnectRepoDialogOpen((preVal) => !preVal)}
|
|
||||||
open={disconnectRepoDialogOpen}
|
open={disconnectRepoDialogOpen}
|
||||||
confirmButtonTitle="Yes, confirm disconnect"
|
|
||||||
handleConfirm={() => {
|
handleConfirm={() => {
|
||||||
setDisconnectRepoDialogOpen((preVal) => !preVal);
|
setDisconnectRepoDialogOpen((preVal) => !preVal);
|
||||||
}}
|
}}
|
||||||
color="red"
|
/>
|
||||||
>
|
|
||||||
<Typography variant="small" placeholder={''}>
|
|
||||||
Any data tied to your Git project may become misconfigured. Are you
|
|
||||||
sure you want to continue?
|
|
||||||
</Typography>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
import { Button, Typography } from '@material-tailwind/react';
|
import { Button } from '@material-tailwind/react';
|
||||||
|
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import { DeleteWebhookDialog } from 'components/projects/Dialog/DeleteWebhookDialog';
|
||||||
|
|
||||||
interface WebhookCardProps {
|
interface WebhookCardProps {
|
||||||
webhookUrl: string;
|
webhookUrl: string;
|
||||||
@ -15,7 +15,6 @@ const WebhookCard = ({ webhookUrl, onDelete }: WebhookCardProps) => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex justify-between w-full mb-3">
|
<div className="flex justify-between w-full mb-3">
|
||||||
{webhookUrl}
|
{webhookUrl}
|
||||||
|
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -38,23 +37,15 @@ const WebhookCard = ({ webhookUrl, onDelete }: WebhookCardProps) => {
|
|||||||
X
|
X
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<DeleteWebhookDialog
|
||||||
<ConfirmDialog
|
handleCancel={() => setDeleteDialogOpen((preVal) => !preVal)}
|
||||||
dialogTitle="Delete webhook"
|
|
||||||
handleOpen={() => setDeleteDialogOpen((preVal) => !preVal)}
|
|
||||||
open={deleteDialogOpen}
|
open={deleteDialogOpen}
|
||||||
confirmButtonTitle="Yes, Confirm delete"
|
|
||||||
handleConfirm={() => {
|
handleConfirm={() => {
|
||||||
setDeleteDialogOpen((preVal) => !preVal);
|
setDeleteDialogOpen((preVal) => !preVal);
|
||||||
onDelete();
|
onDelete();
|
||||||
}}
|
}}
|
||||||
color="red"
|
webhookUrl={webhookUrl}
|
||||||
>
|
/>
|
||||||
<Typography variant="small" placeholder={''}>
|
|
||||||
Are you sure you want to delete the variable{' '}
|
|
||||||
<span className="bg-blue-100 p-0.5 rounded-sm">{webhookUrl}</span>?
|
|
||||||
</Typography>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,64 +1,52 @@
|
|||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
|
import { Modal, ModalProps } from './Modal';
|
||||||
|
import { Button, ButtonOrLinkProps } from './Button';
|
||||||
|
|
||||||
import { color } from '@material-tailwind/react/types/components/button';
|
export type ConfirmDialogProps = ModalProps & {
|
||||||
import {
|
children?: ReactNode;
|
||||||
Typography,
|
dialogTitle?: string;
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogHeader,
|
|
||||||
DialogBody,
|
|
||||||
DialogFooter,
|
|
||||||
} from '@material-tailwind/react';
|
|
||||||
|
|
||||||
type ConfirmDialogProp = {
|
|
||||||
children: React.ReactNode;
|
|
||||||
dialogTitle: string;
|
|
||||||
open: boolean;
|
open: boolean;
|
||||||
handleOpen: () => void;
|
handleCancel: () => void;
|
||||||
confirmButtonTitle: string;
|
confirmButtonTitle?: string;
|
||||||
handleConfirm?: () => void;
|
handleConfirm?: () => void;
|
||||||
color: color;
|
cancelButtonProps?: ButtonOrLinkProps;
|
||||||
|
confirmButtonProps?: ButtonOrLinkProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConfirmDialog = ({
|
const ConfirmDialog = ({
|
||||||
children,
|
children,
|
||||||
dialogTitle,
|
dialogTitle,
|
||||||
open,
|
handleCancel,
|
||||||
handleOpen,
|
|
||||||
confirmButtonTitle,
|
confirmButtonTitle,
|
||||||
handleConfirm,
|
handleConfirm,
|
||||||
color,
|
cancelButtonProps,
|
||||||
}: ConfirmDialogProp) => {
|
confirmButtonProps,
|
||||||
|
...props
|
||||||
|
}: ConfirmDialogProps) => {
|
||||||
|
// Close the dialog when the user clicks outside of it
|
||||||
|
const handleOpenChange = (open: boolean) => {
|
||||||
|
if (!open) return handleCancel?.();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} handler={handleOpen} placeholder={''}>
|
<Modal {...props} onOpenChange={handleOpenChange}>
|
||||||
<DialogHeader className="flex justify-between" placeholder={''}>
|
<Modal.Content>
|
||||||
<Typography variant="h6" placeholder={''}>
|
<Modal.Header>{dialogTitle}</Modal.Header>
|
||||||
{dialogTitle}{' '}
|
<Modal.Body>{children}</Modal.Body>
|
||||||
</Typography>
|
<Modal.Footer>
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="tertiary"
|
||||||
onClick={handleOpen}
|
onClick={handleCancel}
|
||||||
className=" rounded-full"
|
{...cancelButtonProps}
|
||||||
placeholder={''}
|
>
|
||||||
>
|
Cancel
|
||||||
X
|
</Button>
|
||||||
</Button>
|
<Button onClick={handleConfirm} {...confirmButtonProps}>
|
||||||
</DialogHeader>
|
{confirmButtonTitle}
|
||||||
<DialogBody placeholder={''}>{children}</DialogBody>
|
</Button>
|
||||||
<DialogFooter className="flex justify-start gap-2" placeholder={''}>
|
</Modal.Footer>
|
||||||
<Button variant="outlined" onClick={handleOpen} placeholder={''}>
|
</Modal.Content>
|
||||||
Cancel
|
</Modal>
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="gradient"
|
|
||||||
color={color}
|
|
||||||
onClick={handleConfirm}
|
|
||||||
placeholder={''}
|
|
||||||
>
|
|
||||||
{confirmButtonTitle}
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</Dialog>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { CustomIcon, CustomIconProps } from './CustomIcon';
|
||||||
|
|
||||||
|
export const ChevronDoubleDownIcon = (props: CustomIconProps) => {
|
||||||
|
return (
|
||||||
|
<CustomIcon
|
||||||
|
width="20"
|
||||||
|
height="21"
|
||||||
|
viewBox="0 0 20 21"
|
||||||
|
fill="none"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6.66699 12.0174L10.0003 15.3507L13.3337 12.0174M6.66699 6.18408L10.0003 9.51742L13.3337 6.18408"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="square"
|
||||||
|
/>
|
||||||
|
</CustomIcon>
|
||||||
|
);
|
||||||
|
};
|
@ -61,6 +61,7 @@ export * from './CirclePlaceholderOnIcon';
|
|||||||
export * from './WarningTriangleIcon';
|
export * from './WarningTriangleIcon';
|
||||||
export * from './CheckRadioOutlineIcon';
|
export * from './CheckRadioOutlineIcon';
|
||||||
export * from './TrendingIcon';
|
export * from './TrendingIcon';
|
||||||
|
export * from './ChevronDoubleDownIcon';
|
||||||
|
|
||||||
// Templates
|
// Templates
|
||||||
export * from './templates';
|
export * from './templates';
|
||||||
|
73
packages/frontend/src/components/shared/Modal/Modal.theme.ts
Normal file
73
packages/frontend/src/components/shared/Modal/Modal.theme.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import type { VariantProps } from 'tailwind-variants';
|
||||||
|
import { tv } from 'tailwind-variants';
|
||||||
|
|
||||||
|
export const modalTheme = tv({
|
||||||
|
slots: {
|
||||||
|
overlay: [
|
||||||
|
'z-modal',
|
||||||
|
'fixed',
|
||||||
|
'inset-0',
|
||||||
|
'bg-black/80',
|
||||||
|
'backdrop-blur-sm',
|
||||||
|
'overflow-y-auto',
|
||||||
|
'flex',
|
||||||
|
'justify-center',
|
||||||
|
'items-end',
|
||||||
|
'sm:items-center',
|
||||||
|
'p-0',
|
||||||
|
'sm:p-10',
|
||||||
|
'data-[state=closed]:animate-[dialog-overlay-hide_200ms]',
|
||||||
|
'data-[state=open]:animate-[dialog-overlay-show_200ms]',
|
||||||
|
'data-[state=closed]:hidden', // Fix overlay not close when modal is closed
|
||||||
|
],
|
||||||
|
close: ['absolute', 'right-4', 'top-2', 'sm:right-6', 'sm:top-3', 'z-[1]'],
|
||||||
|
header: [
|
||||||
|
'flex',
|
||||||
|
'flex-col',
|
||||||
|
'gap-4',
|
||||||
|
'items-start',
|
||||||
|
'px-4',
|
||||||
|
'py-4',
|
||||||
|
'sm:px-6',
|
||||||
|
'sm:py-5',
|
||||||
|
'bg-base-bg-alternate',
|
||||||
|
],
|
||||||
|
headerTitle: [
|
||||||
|
'text-base',
|
||||||
|
'sm:text-lg',
|
||||||
|
'tracking-[0.011em]',
|
||||||
|
'sm:tracking-normal',
|
||||||
|
'text-elements-high-em',
|
||||||
|
],
|
||||||
|
headerDescription: ['text-sm', 'text-elements-low-em'],
|
||||||
|
footer: ['flex', 'gap-3', 'px-4', 'pb-4', 'pt-7', 'sm:pb-6', 'sm:px-6'],
|
||||||
|
content: [
|
||||||
|
'h-fit',
|
||||||
|
'sm:min-h-0',
|
||||||
|
'sm:m-auto',
|
||||||
|
'relative',
|
||||||
|
'flex',
|
||||||
|
'flex-col',
|
||||||
|
'overflow-hidden',
|
||||||
|
'w-full',
|
||||||
|
'sm:max-w-[562px]',
|
||||||
|
'rounded-2xl',
|
||||||
|
'bg-base-bg',
|
||||||
|
'shadow-card',
|
||||||
|
'text-elements-high-em',
|
||||||
|
],
|
||||||
|
body: ['flex-1', 'px-4', 'pt-4', 'sm:pt-6', 'sm:px-6'],
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
fullPage: {
|
||||||
|
true: {
|
||||||
|
content: ['h-full'],
|
||||||
|
overlay: ['!p-0'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
fullPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export type ModalVariants = VariantProps<typeof modalTheme>;
|
46
packages/frontend/src/components/shared/Modal/Modal.tsx
Normal file
46
packages/frontend/src/components/shared/Modal/Modal.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { DialogProps } from '@radix-ui/react-dialog';
|
||||||
|
import { Root, Trigger } from '@radix-ui/react-dialog';
|
||||||
|
import type { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
import type { ModalVariants } from './Modal.theme';
|
||||||
|
import { ModalBody } from './ModalBody';
|
||||||
|
import { ModalContent } from './ModalContent';
|
||||||
|
import { ModalFooter } from './ModalFooter';
|
||||||
|
import { ModalHeader } from './ModalHeader';
|
||||||
|
import ModalProvider from './ModalProvider';
|
||||||
|
|
||||||
|
export interface ModalProps
|
||||||
|
extends ComponentPropsWithoutRef<'div'>,
|
||||||
|
ModalVariants,
|
||||||
|
DialogProps {
|
||||||
|
hasCloseButton?: boolean;
|
||||||
|
hasOverlay?: boolean;
|
||||||
|
preventClickOutsideToClose?: boolean;
|
||||||
|
}
|
||||||
|
export const Modal = ({
|
||||||
|
children,
|
||||||
|
hasCloseButton = true,
|
||||||
|
hasOverlay = true,
|
||||||
|
preventClickOutsideToClose = false,
|
||||||
|
fullPage = false,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<ModalProps>) => {
|
||||||
|
return (
|
||||||
|
<ModalProvider
|
||||||
|
fullPage={fullPage}
|
||||||
|
hasCloseButton={hasCloseButton}
|
||||||
|
hasOverlay={hasOverlay}
|
||||||
|
preventClickOutsideToClose={preventClickOutsideToClose}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Root {...props}>{children}</Root>
|
||||||
|
</ModalProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Modal.Trigger = Trigger;
|
||||||
|
Modal.Content = ModalContent;
|
||||||
|
Modal.Header = ModalHeader;
|
||||||
|
Modal.Footer = ModalFooter;
|
||||||
|
Modal.Body = ModalBody;
|
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
||||||
|
import { modalTheme } from 'components/shared/Modal/Modal.theme';
|
||||||
|
|
||||||
|
export interface ModalBodyProps extends ComponentPropsWithoutRef<'div'> {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ModalBody = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<ModalBodyProps>) => {
|
||||||
|
const { body } = modalTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={body({
|
||||||
|
className,
|
||||||
|
})}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './ModalBody';
|
@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { DialogContentProps } from '@radix-ui/react-dialog';
|
||||||
|
import { Close, Content, Overlay, Portal } from '@radix-ui/react-dialog';
|
||||||
|
import { Ref, forwardRef, type PropsWithChildren } from 'react';
|
||||||
|
import { useModal } from 'components/shared/Modal/ModalProvider';
|
||||||
|
import { modalTheme } from 'components/shared/Modal/Modal.theme';
|
||||||
|
import { Button } from 'components/shared/Button';
|
||||||
|
import { CrossIcon } from 'components/shared/CustomIcon';
|
||||||
|
|
||||||
|
type PointerDownOutsideEvent = CustomEvent<{
|
||||||
|
originalEvent: PointerEvent;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export interface ModalContentProps extends DialogContentProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModalContent = forwardRef(
|
||||||
|
(
|
||||||
|
{ children, className, ...props }: PropsWithChildren<ModalContentProps>,
|
||||||
|
forwardedRef,
|
||||||
|
) => {
|
||||||
|
const { hasCloseButton, preventClickOutsideToClose, fullPage } = useModal();
|
||||||
|
|
||||||
|
const { content, close, overlay } = modalTheme({ fullPage });
|
||||||
|
|
||||||
|
const preventClickOutsideToCloseProps = preventClickOutsideToClose && {
|
||||||
|
onPointerDownOutside: (e: PointerDownOutsideEvent) => e.preventDefault(),
|
||||||
|
onEscapeKeyDown: (e: KeyboardEvent) => e.preventDefault(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<Overlay className={overlay({ fullPage })}>
|
||||||
|
<Content
|
||||||
|
className={content({ className, fullPage })}
|
||||||
|
{...preventClickOutsideToCloseProps}
|
||||||
|
{...props}
|
||||||
|
ref={forwardedRef as Ref<HTMLDivElement>}
|
||||||
|
>
|
||||||
|
{hasCloseButton && (
|
||||||
|
<Close asChild>
|
||||||
|
<Button
|
||||||
|
aria-label="Close"
|
||||||
|
className={close()}
|
||||||
|
size="sm"
|
||||||
|
iconOnly
|
||||||
|
variant="tertiary"
|
||||||
|
>
|
||||||
|
<CrossIcon />
|
||||||
|
</Button>
|
||||||
|
</Close>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</Content>
|
||||||
|
</Overlay>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ModalContent.displayName = 'ModalContent';
|
||||||
|
|
||||||
|
export { ModalContent };
|
@ -0,0 +1 @@
|
|||||||
|
export * from './ModalContent';
|
@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
||||||
|
import { modalTheme } from 'components/shared/Modal/Modal.theme';
|
||||||
|
|
||||||
|
type ModalFooterProps = ComponentPropsWithoutRef<'div'> & {
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ModalFooter = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<ModalFooterProps>) => {
|
||||||
|
const { footer } = modalTheme({
|
||||||
|
className,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className={footer({ className })} {...props}>
|
||||||
|
{children}
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './ModalFooter';
|
@ -0,0 +1,56 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { DialogDescriptionProps } from '@radix-ui/react-dialog';
|
||||||
|
import { Description, Title } from '@radix-ui/react-dialog';
|
||||||
|
import type { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
||||||
|
import { Heading } from 'components/shared/Heading';
|
||||||
|
import { modalTheme } from 'components/shared/Modal/Modal.theme';
|
||||||
|
import { WavyBorder } from 'components/shared/WavyBorder';
|
||||||
|
|
||||||
|
type ModalHeaderProps = ComponentPropsWithoutRef<'div'> & {
|
||||||
|
className?: string;
|
||||||
|
description?: string | React.ReactNode;
|
||||||
|
descriptionProps?: DialogDescriptionProps;
|
||||||
|
headingProps?: ComponentPropsWithoutRef<'h2'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ModalHeader = ({
|
||||||
|
children,
|
||||||
|
description,
|
||||||
|
className,
|
||||||
|
descriptionProps,
|
||||||
|
headingProps,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<ModalHeaderProps>) => {
|
||||||
|
const { header, headerDescription, headerTitle } = modalTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={header({
|
||||||
|
className,
|
||||||
|
})}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Title asChild>
|
||||||
|
<Heading
|
||||||
|
{...headingProps}
|
||||||
|
className={headerTitle({ className: headingProps?.className })}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Heading>
|
||||||
|
</Title>
|
||||||
|
{description && (
|
||||||
|
<Description
|
||||||
|
{...descriptionProps}
|
||||||
|
className={headerDescription({
|
||||||
|
className: descriptionProps?.className,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{description}
|
||||||
|
</Description>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<WavyBorder className="-mt-0.5" variant="stroke" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './ModalHeader';
|
@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
import type { ModalProps } from './Modal';
|
||||||
|
import type { ModalVariants } from './Modal.theme';
|
||||||
|
|
||||||
|
export interface ModalProviderProps
|
||||||
|
extends Partial<ModalVariants>,
|
||||||
|
ModalProps {}
|
||||||
|
|
||||||
|
type ModalProviderContext = ReturnType<typeof useModalValues>;
|
||||||
|
|
||||||
|
const ModalContext = createContext<Partial<ModalProviderContext> | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
// For inferring return type
|
||||||
|
const useModalValues = (props: ModalProviderProps) => {
|
||||||
|
return props;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ModalProvider = ({
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<ModalProviderProps>): JSX.Element => {
|
||||||
|
const values = useModalValues(props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContext.Provider value={values}>{children}</ModalContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useModal = () => {
|
||||||
|
const context = useContext(ModalContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useModal was used outside of its Provider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalProvider;
|
1
packages/frontend/src/components/shared/Modal/index.ts
Normal file
1
packages/frontend/src/components/shared/Modal/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './Modal';
|
@ -6,7 +6,7 @@ import React, {
|
|||||||
import { tagTheme, type TagTheme } from './Tag.theme';
|
import { tagTheme, type TagTheme } from './Tag.theme';
|
||||||
import { cloneIcon } from 'utils/cloneIcon';
|
import { cloneIcon } from 'utils/cloneIcon';
|
||||||
|
|
||||||
type TagProps = ComponentPropsWithoutRef<'div'> &
|
export type TagProps = ComponentPropsWithoutRef<'div'> &
|
||||||
TagTheme & {
|
TagTheme & {
|
||||||
/**
|
/**
|
||||||
* The optional left icon element for a component.
|
* The optional left icon element for a component.
|
||||||
|
@ -153,4 +153,44 @@
|
|||||||
.focus-ring {
|
.focus-ring {
|
||||||
@apply focus-visible:ring-[3px] focus-visible:ring-snowball-200 focus-visible:ring-offset-1 focus-visible:ring-offset-snowball-500 focus-visible:outline-none;
|
@apply focus-visible:ring-[3px] focus-visible:ring-snowball-200 focus-visible:ring-offset-1 focus-visible:ring-offset-snowball-500 focus-visible:outline-none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes dialog-overlay-show {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dialog-overlay-hide {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dialog-content-show {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, -50%) scale(0.95);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, -50%) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dialog-content-hide {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, -50%) scale(1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, -50%) scale(0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ const ProjectSearch = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<section className="h-full z-0">
|
<section className="h-full z-0 overflow-y-auto">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
@ -35,6 +35,8 @@ import {
|
|||||||
import { renderDefaultTag, renderMinimalTag } from './renders/tag';
|
import { renderDefaultTag, renderMinimalTag } from './renders/tag';
|
||||||
import { renderToast, renderToastsWithCta } from './renders/toast';
|
import { renderToast, renderToastsWithCta } from './renders/toast';
|
||||||
import { renderTooltips } from './renders/tooltip';
|
import { renderTooltips } from './renders/tooltip';
|
||||||
|
import { Button } from 'components/shared/Button';
|
||||||
|
import { Modal } from 'components/shared/Modal';
|
||||||
|
|
||||||
const Page: React.FC = () => {
|
const Page: React.FC = () => {
|
||||||
const [singleDate, setSingleDate] = useState<Value>();
|
const [singleDate, setSingleDate] = useState<Value>();
|
||||||
@ -57,6 +59,32 @@ const Page: React.FC = () => {
|
|||||||
|
|
||||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||||
|
|
||||||
|
{/* Modal */}
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
<h1 className="text-2xl font-bold">Modal</h1>
|
||||||
|
<div className="flex gap-4 flex-wrap items-center justify-center">
|
||||||
|
{/* Modal example */}
|
||||||
|
<Modal>
|
||||||
|
<Modal.Trigger asChild>
|
||||||
|
<Button>Open modal</Button>
|
||||||
|
</Modal.Trigger>
|
||||||
|
<Modal.Content>
|
||||||
|
<Modal.Header>Modal title</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<p>Modal content</p>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button>Close</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal.Content>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||||
|
|
||||||
{/* Steps */}
|
{/* Steps */}
|
||||||
<div className="flex flex-col gap-10 items-center justify-between">
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
<div className="flex flex-col gap-10 items-center justify-between">
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
274
packages/frontend/src/pages/components/modals.tsx
Normal file
274
packages/frontend/src/pages/components/modals.tsx
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { Button } from 'components/shared/Button';
|
||||||
|
import { Modal } from 'components/shared/Modal';
|
||||||
|
import { TransferProjectDialog } from 'components/projects/Dialog/TransferProjectDialog';
|
||||||
|
import { DeleteWebhookDialog } from 'components/projects/Dialog/DeleteWebhookDialog';
|
||||||
|
import { DisconnectRepositoryDialog } from 'components/projects/Dialog/DisconnectRepositoryDialog';
|
||||||
|
import { RemoveMemberDialog } from 'components/projects/Dialog/RemoveMemberDialog';
|
||||||
|
import { DeleteVariableDialog } from 'components/projects/Dialog/DeleteVariableDialog';
|
||||||
|
import { DeleteDomainDialog } from 'components/projects/Dialog/DeleteDomainDialog';
|
||||||
|
import { CancelDeploymentDialog } from 'components/projects/Dialog/CancelDeploymentDialog';
|
||||||
|
import {
|
||||||
|
Deployment,
|
||||||
|
DeploymentStatus,
|
||||||
|
Domain,
|
||||||
|
DomainStatus,
|
||||||
|
Environment,
|
||||||
|
} from 'gql-client';
|
||||||
|
import { ChangeStateToProductionDialog } from 'components/projects/Dialog/ChangeStateToProductionDialog';
|
||||||
|
|
||||||
|
const deployment: Deployment = {
|
||||||
|
id: '1',
|
||||||
|
domain: {
|
||||||
|
id: 'domain1',
|
||||||
|
branch: 'main',
|
||||||
|
name: 'example.com',
|
||||||
|
status: DomainStatus.Live,
|
||||||
|
redirectTo: null,
|
||||||
|
createdAt: '1677609600', // 2023-02-25T12:00:00Z
|
||||||
|
updatedAt: '1677613200', // 2023-02-25T13:00:00Z
|
||||||
|
},
|
||||||
|
branch: 'main',
|
||||||
|
commitHash: 'a1b2c3d',
|
||||||
|
commitMessage:
|
||||||
|
'lkajsdlakjsdlaijwlkjadlksjdlaisjdlakjswdalijsdlaksdj lakjsdlasjdlaijwdel akjsdlaj sldkjaliwjdeal ksjdla ijsdlaksjd',
|
||||||
|
url: 'https://deploy1.example.com',
|
||||||
|
environment: Environment.Production,
|
||||||
|
isCurrent: true,
|
||||||
|
status: DeploymentStatus.Ready,
|
||||||
|
createdBy: {
|
||||||
|
id: 'user1',
|
||||||
|
name: 'Alice',
|
||||||
|
email: 'alice@example.com',
|
||||||
|
isVerified: true,
|
||||||
|
createdAt: '1672656000', // 2023-01-01T10:00:00Z
|
||||||
|
updatedAt: '1672659600', // 2023-01-01T11:00:00Z
|
||||||
|
gitHubToken: null,
|
||||||
|
},
|
||||||
|
createdAt: '1677676800', // 2023-03-01T12:00:00Z
|
||||||
|
updatedAt: '1677680400', // 2023-03-01T13:00:00Z
|
||||||
|
};
|
||||||
|
|
||||||
|
const domains: Domain[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
branch: 'main',
|
||||||
|
name: 'saugat.com',
|
||||||
|
status: DomainStatus.Live,
|
||||||
|
redirectTo: null,
|
||||||
|
createdAt: '1677676800', // 2023-03-01T12:00:00Z
|
||||||
|
updatedAt: '1677680400', // 2023-03-01T13:00:00Z
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
branch: 'main',
|
||||||
|
name: 'www.saugat.com',
|
||||||
|
status: DomainStatus.Live,
|
||||||
|
redirectTo: null,
|
||||||
|
createdAt: '1677676800', // 2023-03-01T12:00:00Z
|
||||||
|
updatedAt: '1677680400', // 2023-03-01T13:00:00Z
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ModalsPage: React.FC = () => {
|
||||||
|
const [openTransferDialog, setOpenTransferDialog] = useState(false);
|
||||||
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
|
const [disconnectRepoDialogOpen, setDisconnectRepoDialogOpen] =
|
||||||
|
useState(false);
|
||||||
|
const [removeMemberDialogOpen, setRemoveMemberDialogOpen] = useState(false);
|
||||||
|
const [deleteVariableDialogOpen, setDeleteVariableDialogOpen] =
|
||||||
|
useState(false);
|
||||||
|
const [deleteDomainDialogOpen, setDeleteDomainDialogOpen] = useState(false);
|
||||||
|
const [cancelDeploymentDialogOpen, setCancelDeploymentDialogOpen] =
|
||||||
|
useState(false);
|
||||||
|
const [changeProductionDialogOpen, setChangeProductionDialogOpen] =
|
||||||
|
useState(false);
|
||||||
|
const [redeployToProduction, setRedeployToProduction] = useState(false);
|
||||||
|
const [rollbackDeployment, setRollbackDeployment] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative h-full min-h-full">
|
||||||
|
<div className="flex flex-col items-center justify-center container mx-auto px-20 py-20">
|
||||||
|
<h1 className="text-4xl font-bold">Manual Storybook</h1>
|
||||||
|
<p className="mt-4 text-lg text-center text-gray-500">
|
||||||
|
Get started by editing{' '}
|
||||||
|
<code className="p-2 font-mono text-sm bg-gray-100 rounded-md">
|
||||||
|
packages/frontend/src/pages/components/index.tsx
|
||||||
|
</code>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||||
|
|
||||||
|
{/* Modal */}
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
<h1 className="text-2xl font-bold">Modal</h1>
|
||||||
|
<div className="flex gap-4 flex-wrap items-center justify-center">
|
||||||
|
{/* Modal example */}
|
||||||
|
<Modal>
|
||||||
|
<Modal.Trigger asChild>
|
||||||
|
<Button>Open modal</Button>
|
||||||
|
</Modal.Trigger>
|
||||||
|
<Modal.Content>
|
||||||
|
<Modal.Header>Modal title</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<p>Modal content</p>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button>Close</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal.Content>
|
||||||
|
</Modal>
|
||||||
|
{/* Transfer project */}
|
||||||
|
<Button onClick={() => setOpenTransferDialog(true)}>
|
||||||
|
Transfer project
|
||||||
|
</Button>
|
||||||
|
<TransferProjectDialog
|
||||||
|
handleCancel={() => setOpenTransferDialog(!openTransferDialog)}
|
||||||
|
open={openTransferDialog}
|
||||||
|
handleConfirm={() => setOpenTransferDialog(!openTransferDialog)}
|
||||||
|
projectName="nextjs-boilerplate"
|
||||||
|
from="ayungavis"
|
||||||
|
to="Airfoil"
|
||||||
|
/>
|
||||||
|
{/* Delete webhook */}
|
||||||
|
<Button onClick={() => setDeleteDialogOpen(true)}>
|
||||||
|
Delete webhook
|
||||||
|
</Button>
|
||||||
|
<DeleteWebhookDialog
|
||||||
|
handleCancel={() => setDeleteDialogOpen((preVal) => !preVal)}
|
||||||
|
open={deleteDialogOpen}
|
||||||
|
handleConfirm={() => setDeleteDialogOpen((preVal) => !preVal)}
|
||||||
|
webhookUrl="examplehook.com"
|
||||||
|
/>
|
||||||
|
{/* Disconnect repository */}
|
||||||
|
<Button onClick={() => setDisconnectRepoDialogOpen(true)}>
|
||||||
|
Disconnect repository
|
||||||
|
</Button>
|
||||||
|
<DisconnectRepositoryDialog
|
||||||
|
handleCancel={() =>
|
||||||
|
setDisconnectRepoDialogOpen((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
open={disconnectRepoDialogOpen}
|
||||||
|
handleConfirm={() => {
|
||||||
|
setDisconnectRepoDialogOpen((preVal) => !preVal);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Remove member */}
|
||||||
|
<Button onClick={() => setRemoveMemberDialogOpen(true)}>
|
||||||
|
Remove member
|
||||||
|
</Button>
|
||||||
|
<RemoveMemberDialog
|
||||||
|
dialogTitle="Remove member?"
|
||||||
|
handleCancel={() =>
|
||||||
|
setRemoveMemberDialogOpen((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
open={removeMemberDialogOpen}
|
||||||
|
confirmButtonTitle="Yes, Remove member"
|
||||||
|
handleConfirm={() =>
|
||||||
|
setRemoveMemberDialogOpen((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
memberName="John Doe"
|
||||||
|
ethAddress="0x1234567890"
|
||||||
|
emailDomain="example.com"
|
||||||
|
/>
|
||||||
|
{/* Delete variable */}
|
||||||
|
<Button onClick={() => setDeleteVariableDialogOpen(true)}>
|
||||||
|
Delete variable
|
||||||
|
</Button>
|
||||||
|
<DeleteVariableDialog
|
||||||
|
handleCancel={() =>
|
||||||
|
setDeleteVariableDialogOpen((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
open={deleteVariableDialogOpen}
|
||||||
|
handleConfirm={() =>
|
||||||
|
setDeleteVariableDialogOpen((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
variableKey="AIUTH_TOKEN"
|
||||||
|
/>
|
||||||
|
{/* Delete domain */}
|
||||||
|
<Button onClick={() => setDeleteDomainDialogOpen(true)}>
|
||||||
|
Delete domain
|
||||||
|
</Button>
|
||||||
|
<DeleteDomainDialog
|
||||||
|
handleCancel={() =>
|
||||||
|
setDeleteDomainDialogOpen((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
open={deleteDomainDialogOpen}
|
||||||
|
handleConfirm={() =>
|
||||||
|
setDeleteDomainDialogOpen((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
projectName="Airfoil"
|
||||||
|
domainName="airfoil.com"
|
||||||
|
/>
|
||||||
|
{/* Cancel deployment */}
|
||||||
|
<Button onClick={() => setCancelDeploymentDialogOpen(true)}>
|
||||||
|
Cancel deployment
|
||||||
|
</Button>
|
||||||
|
<CancelDeploymentDialog
|
||||||
|
handleCancel={() =>
|
||||||
|
setCancelDeploymentDialogOpen(!cancelDeploymentDialogOpen)
|
||||||
|
}
|
||||||
|
open={cancelDeploymentDialogOpen}
|
||||||
|
handleConfirm={() =>
|
||||||
|
setCancelDeploymentDialogOpen(!cancelDeploymentDialogOpen)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{/* Change to production */}
|
||||||
|
<Button onClick={() => setChangeProductionDialogOpen(true)}>
|
||||||
|
Change to production
|
||||||
|
</Button>
|
||||||
|
<ChangeStateToProductionDialog
|
||||||
|
dialogTitle="Change to production?"
|
||||||
|
confirmButtonTitle="Change"
|
||||||
|
handleCancel={() => setChangeProductionDialogOpen(false)}
|
||||||
|
open={changeProductionDialogOpen}
|
||||||
|
handleConfirm={() => setChangeProductionDialogOpen(false)}
|
||||||
|
deployment={deployment}
|
||||||
|
domains={domains}
|
||||||
|
/>
|
||||||
|
{/* Redeploy to production */}
|
||||||
|
<Button onClick={() => setRedeployToProduction(true)}>
|
||||||
|
Redeploy to production
|
||||||
|
</Button>
|
||||||
|
<ChangeStateToProductionDialog
|
||||||
|
dialogTitle="Redeploy to production?"
|
||||||
|
handleCancel={() =>
|
||||||
|
setRedeployToProduction((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
open={redeployToProduction}
|
||||||
|
confirmButtonTitle="Redeploy"
|
||||||
|
handleConfirm={async () =>
|
||||||
|
setRedeployToProduction((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
deployment={deployment}
|
||||||
|
domains={deployment.domain ? [deployment.domain] : []}
|
||||||
|
/>
|
||||||
|
{/* Rollback to this deployment */}
|
||||||
|
<Button onClick={() => setRollbackDeployment(true)}>
|
||||||
|
Rollback to this deployment
|
||||||
|
</Button>
|
||||||
|
<ChangeStateToProductionDialog
|
||||||
|
dialogTitle="Rollback to this deployment?"
|
||||||
|
handleCancel={() => setRollbackDeployment((preVal) => !preVal)}
|
||||||
|
open={rollbackDeployment}
|
||||||
|
confirmButtonTitle="Rollback"
|
||||||
|
handleConfirm={async () =>
|
||||||
|
setRollbackDeployment((preVal) => !preVal)
|
||||||
|
}
|
||||||
|
deployment={deployment}
|
||||||
|
newDeployment={deployment}
|
||||||
|
domains={deployment.domain ? [deployment.domain] : []}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalsPage;
|
@ -6,11 +6,11 @@ import { Organization } from 'gql-client';
|
|||||||
|
|
||||||
import { Button, Typography, Input, Option } from '@material-tailwind/react';
|
import { Button, Typography, Input, Option } from '@material-tailwind/react';
|
||||||
|
|
||||||
import DeleteProjectDialog from '../../../../../components/projects/project/settings/DeleteProjectDialog';
|
import DeleteProjectDialog from 'components/projects/project/settings/DeleteProjectDialog';
|
||||||
import ConfirmDialog from '../../../../../components/shared/ConfirmDialog';
|
import { useGQLClient } from 'context/GQLClientContext';
|
||||||
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
import AsyncSelect from 'components/shared/AsyncSelect';
|
||||||
import AsyncSelect from '../../../../../components/shared/AsyncSelect';
|
import { OutletContextType } from 'types';
|
||||||
import { OutletContextType } from '../../../../../types';
|
import { TransferProjectDialog } from 'components/projects/Dialog/TransferProjectDialog';
|
||||||
|
|
||||||
const CopyIcon = ({ value }: { value: string }) => {
|
const CopyIcon = ({ value }: { value: string }) => {
|
||||||
return (
|
return (
|
||||||
@ -230,19 +230,14 @@ const GeneralTabPanel = () => {
|
|||||||
Transfer
|
Transfer
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
<ConfirmDialog
|
<TransferProjectDialog
|
||||||
dialogTitle="Transfer project"
|
handleCancel={() => setOpenTransferDialog(!openTransferDialog)}
|
||||||
handleOpen={() => setOpenTransferDialog(!openTransferDialog)}
|
|
||||||
open={openTransferDialog}
|
open={openTransferDialog}
|
||||||
confirmButtonTitle="Yes, Confirm transfer"
|
|
||||||
handleConfirm={handleTransferProject}
|
handleConfirm={handleTransferProject}
|
||||||
color="blue"
|
projectName={project.name}
|
||||||
>
|
from={project.organization.name}
|
||||||
<Typography variant="small" placeholder={''}>
|
to={selectedUserOrgName}
|
||||||
Upon confirmation, your project {project.name} will be transferred
|
/>
|
||||||
from {project.organization.name} to {selectedUserOrgName}.
|
|
||||||
</Typography>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-1">
|
<div className="mb-1">
|
||||||
<Typography variant="h6" placeholder={''}>
|
<Typography variant="h6" placeholder={''}>
|
||||||
|
Loading…
Reference in New Issue
Block a user