Add transfer and delete project in settings tab panel (#22)

* Add dialogs for transfer and delete project

* Refactor confirm dialog

* Handle form state of transfer and delete dialog

* Handle default value of transfer dropdown

* Add space in delete dialog body

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2023-12-22 12:31:30 +05:30 committed by GitHub
parent 5dc079bc8f
commit e93cca598a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 303 additions and 88 deletions

View File

@ -1,51 +0,0 @@
import React from 'react';
import { Link } from 'react-router-dom';
import {
Button,
Dialog,
DialogHeader,
DialogBody,
DialogFooter,
} from '@material-tailwind/react';
interface CancelDeploymentDialogProp {
open: boolean;
handleOpen: () => void;
}
const CancelDeploymentDialog = ({
open,
handleOpen,
}: CancelDeploymentDialogProp) => {
return (
<Dialog open={open} handler={handleOpen}>
<DialogHeader className="flex justify-between">
<div>Cancel deployment?</div>
<Button
variant="outlined"
onClick={handleOpen}
className="mr-1 rounded-3xl"
>
<span>X</span>
</Button>
</DialogHeader>
<DialogBody>
This will halt the deployment and you will have to start the process
from scratch.
</DialogBody>
<DialogFooter className="flex justify-center">
<Button variant="outlined" onClick={handleOpen} className="mr-1">
<span>Cancel</span>
</Button>
<Link to="/projects/create/template">
<Button variant="gradient" color="red" onClick={handleOpen}>
<span>Yes, Cancel deployment</span>
</Button>
</Link>
</DialogFooter>
</Dialog>
);
};
export default CancelDeploymentDialog;

View File

@ -52,9 +52,13 @@ const tabsData = [
const SettingsTabPanel = () => { const SettingsTabPanel = () => {
return ( return (
<> <>
<Tabs value={'general'} orientation="vertical" className="my-6"> <Tabs
value={'general'}
orientation="vertical"
className="grid grid-cols-4"
>
<TabsHeader <TabsHeader
className="w-60 bg-transparent" className="bg-transparent col-span-1"
indicatorProps={{ indicatorProps={{
className: 'bg-gray-900/10 shadow-none !text-gray-900', className: 'bg-gray-900/10 shadow-none !text-gray-900',
}} }}
@ -68,7 +72,7 @@ const SettingsTabPanel = () => {
</Tab> </Tab>
))} ))}
</TabsHeader> </TabsHeader>
<TabsBody> <TabsBody className="col-span-2">
{tabsData.map(({ value, component }) => ( {tabsData.map(({ value, component }) => (
<TabPanel key={value} value={value} className="p-2"> <TabPanel key={value} value={value} className="p-2">
{createElement(component)} {createElement(component)}

View File

@ -0,0 +1,95 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import {
Button,
Dialog,
DialogHeader,
DialogBody,
DialogFooter,
Input,
Typography,
} from '@material-tailwind/react';
import { ProjectDetails } from '../../../../types/project';
interface DeleteProjectDialogProp {
open: boolean;
handleOpen: () => void;
project: Partial<ProjectDetails>;
}
const DeleteProjectDialog = ({
open,
handleOpen,
project,
}: DeleteProjectDialogProp) => {
const navigate = useNavigate();
const {
handleSubmit,
register,
formState: { isValid },
} = useForm({
defaultValues: {
projectName: '',
},
});
return (
<Dialog open={open} handler={handleOpen}>
<DialogHeader className="flex justify-between">
<div>Delete project?</div>
<Button
variant="outlined"
onClick={handleOpen}
className="mr-1 rounded-3xl"
>
X
</Button>
</DialogHeader>
<form
onSubmit={handleSubmit(() => {
handleOpen();
navigate('/');
})}
>
<DialogBody className="flex flex-col gap-2">
<Typography variant="paragraph">
Deleting your project is irreversible. Enter your projects name
&nbsp;
<span className="bg-blue-100 text-blue-700">({project.name})</span>
&nbsp;below to confirm you want to permanently delete it:
</Typography>
<Input
id="input"
crossOrigin={undefined}
{...register('projectName', {
required: 'Project name is required',
validate: (value) => value === project.name,
})}
/>
<Typography variant="small" color="red">
^ Deleting your project is irreversible.
</Typography>
</DialogBody>
<DialogFooter className="flex justify-start">
<Button variant="outlined" onClick={handleOpen} className="mr-1">
Cancel
</Button>
<Button
variant="gradient"
color="red"
type="submit"
disabled={!isValid}
>
Yes, Delete project
</Button>
</DialogFooter>
</form>
</Dialog>
);
};
export default DeleteProjectDialog;

View File

@ -1,10 +1,22 @@
import React from 'react'; import React, { useState } from 'react';
import { useForm } from 'react-hook-form'; import { Link } from 'react-router-dom';
import { useForm, Controller } from 'react-hook-form';
import toast, { Toaster } from 'react-hot-toast'; import toast, { Toaster } from 'react-hot-toast';
import { Button, Typography, Input } from '@material-tailwind/react'; import {
Button,
Typography,
Input,
Select,
Option,
} from '@material-tailwind/react';
import DeleteProjectDialog from './DeleteProjectDialog';
import ConfirmDialog from '../../../shared/ConfirmDialog';
const PROJECT_ID = '62f87575-7a2b-4951-8156-9f9821j380d'; const PROJECT_ID = '62f87575-7a2b-4951-8156-9f9821j380d';
const TEAMS = ['Airfoil'];
const DEFAULT_SELECT_TEAM = undefined;
const CopyIcon = ({ value }: { value: string }) => { const CopyIcon = ({ value }: { value: string }) => {
return ( return (
@ -21,6 +33,24 @@ const CopyIcon = ({ value }: { value: string }) => {
}; };
const GeneralTabPanel = () => { const GeneralTabPanel = () => {
const {
handleSubmit: handleTransfer,
control,
formState,
} = useForm({
defaultValues: {
team: DEFAULT_SELECT_TEAM,
},
});
const [openTransferDialog, setOpenTransferDialog] = useState(false);
const handleTransferProjectDialog = () =>
setOpenTransferDialog(!openTransferDialog);
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
const handleDeleteProjectDialog = () =>
setOpenDeleteDialog(!openDeleteDialog);
const { handleSubmit, register } = useForm({ const { handleSubmit, register } = useForm({
defaultValues: { defaultValues: {
appName: 'iglootools', appName: 'iglootools',
@ -29,17 +59,12 @@ const GeneralTabPanel = () => {
}); });
return ( return (
<form onSubmit={handleSubmit(() => {})}> <>
<div className="mb-4"> <form onSubmit={handleSubmit(() => {})}>
<Typography variant="h6">Project info</Typography> <Typography variant="h6">Project info</Typography>
</div> <Typography variant="small" className="font-medium text-gray-800">
<div className="my-2 w-3/5">
<label
htmlFor="input"
className="block text-sm font-medium text-gray-800"
>
App name App name
</label> </Typography>
<Input <Input
id="input" id="input"
variant="outlined" variant="outlined"
@ -48,14 +73,9 @@ const GeneralTabPanel = () => {
size="md" size="md"
{...register('appName')} {...register('appName')}
/> />
</div> <Typography variant="small" className="font-medium text-gray-800">
<div className="my-2 w-3/5">
<label
htmlFor="input"
className="block text-sm font-medium text-gray-800"
>
Description (Optional) Description (Optional)
</label> </Typography>
<Input <Input
id="input" id="input"
variant="outlined" variant="outlined"
@ -63,14 +83,9 @@ const GeneralTabPanel = () => {
size="md" size="md"
{...register('description')} {...register('description')}
/> />
</div> <Typography variant="small" className="font-medium text-gray-800">
<div className="my-2 w-3/5">
<label
htmlFor="input"
className="block text-sm font-medium text-gray-800"
>
Project ID Project ID
</label> </Typography>
<Input <Input
id="input" id="input"
crossOrigin={undefined} crossOrigin={undefined}
@ -80,14 +95,91 @@ const GeneralTabPanel = () => {
disabled disabled
icon={<CopyIcon value={PROJECT_ID} />} icon={<CopyIcon value={PROJECT_ID} />}
/> />
</div> <Button type="submit" variant="gradient" size="sm" className="mt-1">
<div>
<Button type="submit" variant="gradient" size="sm">
Save Save
</Button> </Button>
</form>
<div className="mb-1">
<Typography variant="h6">Transfer project</Typography>
<Typography variant="small">
Transfer this app to your personal account or a team you are a member
of.
<Link to="" className="text-blue-500">
Learn more
</Link>
</Typography>
<form
onSubmit={handleTransfer(() => {
handleTransferProjectDialog();
})}
>
<Typography variant="small" className="font-medium text-gray-800">
Choose team
</Typography>
<Controller
name="team"
rules={{ required: 'This field is required' }}
control={control}
render={({ field }) => (
<Select
{...field}
// TODO: Implement placeholder for select
label={!field.value ? 'Select an account / team' : ''}
>
{TEAMS.map((team, key) => (
<Option key={key} value={team}>
^ {team}
</Option>
))}
</Select>
)}
/>
<Button
variant="gradient"
size="sm"
className="mt-1"
disabled={!formState.isValid}
type="submit"
>
Transfer
</Button>
</form>
<ConfirmDialog
dialogTitle="Transfer project"
handleOpen={handleTransferProjectDialog}
open={openTransferDialog}
confirmButtonTitle="Yes, Confirm transfer"
handleConfirm={handleTransferProjectDialog}
color="blue"
>
<Typography variant="small">
Upon confirmation, your project nextjs-boilerplate will be
transferred from saugat to Airfoil.
</Typography>
</ConfirmDialog>
</div>
<div className="mb-1">
<Typography variant="h6">Delete project</Typography>
<Typography variant="small">
The project will be permanently deleted, including its deployments and
domains. This action is irreversible and can not be undone.
</Typography>
<Button
variant="gradient"
size="sm"
color="red"
onClick={handleDeleteProjectDialog}
>
^ Delete project
</Button>
<DeleteProjectDialog
handleOpen={handleDeleteProjectDialog}
open={openDeleteDialog}
project={{ name: 'Iglootools' }}
/>
</div> </div>
<Toaster /> <Toaster />
</form> </>
); );
}; };

View File

@ -0,0 +1,57 @@
import React from 'react';
import { color } from '@material-tailwind/react/types/components/button';
import {
Typography,
Button,
Dialog,
DialogHeader,
DialogBody,
DialogFooter,
} from '@material-tailwind/react';
type ConfirmDialogProp = {
children: React.ReactNode;
dialogTitle: string;
open: boolean;
handleOpen: () => void;
confirmButtonTitle: string;
handleConfirm: () => void;
color: color;
};
const ConfirmDialog = ({
children,
dialogTitle,
open,
handleOpen,
confirmButtonTitle,
handleConfirm,
color,
}: ConfirmDialogProp) => {
return (
<Dialog open={open} handler={handleOpen}>
<DialogHeader className="flex justify-between">
<Typography variant="h6">{dialogTitle} </Typography>
<Button
variant="outlined"
onClick={handleOpen}
className=" rounded-full"
>
X
</Button>
</DialogHeader>
<DialogBody>{children}</DialogBody>
<DialogFooter className="flex justify-start gap-2">
<Button variant="outlined" onClick={handleOpen}>
Cancel
</Button>
<Button variant="gradient" color={color} onClick={handleConfirm}>
{confirmButtonTitle}
</Button>
</DialogFooter>
</Dialog>
);
};
export default ConfirmDialog;

View File

@ -1,6 +1,7 @@
import React from 'react'; import React, { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Button } from '@material-tailwind/react'; import { Button, Typography } from '@material-tailwind/react';
import { import {
DeployStep, DeployStep,
@ -10,11 +11,16 @@ import {
Stopwatch, Stopwatch,
setStopWatchOffset, setStopWatchOffset,
} from '../../../../components/StopWatch'; } from '../../../../components/StopWatch';
import CancelDeploymentDialog from '../../../../components/projects/create/template/deploy/CancelDeploymentDialog'; import ConfirmDialog from '../../../../components/shared/ConfirmDialog';
const Deploy = () => { const Deploy = () => {
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(!open); const handleOpen = () => setOpen(!open);
const navigate = useNavigate();
const handleCancel = useCallback(() => {
navigate('/projects/create/template');
}, []);
return ( return (
<div> <div>
@ -33,7 +39,19 @@ const Deploy = () => {
^ Cancel ^ Cancel
</Button> </Button>
</div> </div>
<CancelDeploymentDialog handleOpen={handleOpen} open={open} /> <ConfirmDialog
dialogTitle="Cancel deployment?"
handleOpen={handleOpen}
open={open}
confirmButtonTitle="Yes, Cancel deployment"
handleConfirm={handleCancel}
color="red"
>
<Typography variant="small">
This will halt the deployment and you will have to start the process
from scratch.
</Typography>
</ConfirmDialog>
</div> </div>
<DeployStep <DeployStep
title="Building" title="Building"