forked from cerc-io/snowballtools-base
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:
parent
5dc079bc8f
commit
e93cca598a
@ -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;
|
|
@ -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)}
|
||||||
|
@ -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 project’s name
|
||||||
|
|
||||||
|
<span className="bg-blue-100 text-blue-700">({project.name})</span>
|
||||||
|
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;
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
57
packages/frontend/src/components/shared/ConfirmDialog.tsx
Normal file
57
packages/frontend/src/components/shared/ConfirmDialog.tsx
Normal 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;
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user