Add dialog for editing domains in project settings tab (#38)

* Implement edit domain dialog

* Pass project and repo from domain component

* Fix dialog states in domain card

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
2024-01-08 10:48:07 +05:30
committed by GitHub
co-authored by neeraj
parent 2cb1feedcb
commit 66aa8fed4f
7 changed files with 236 additions and 33 deletions
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import toast from 'react-hot-toast';
import {
Chip,
Typography,
@@ -11,9 +11,14 @@ import {
Card,
} from '@material-tailwind/react';
import { DomainDetails, DomainStatus } from '../../../../types/project';
import {
DomainDetails,
DomainStatus,
ProjectDetails,
RepositoryDetails,
} from '../../../../types/project';
import ConfirmDialog from '../../../shared/ConfirmDialog';
import projectData from '../../../../assets/projects.json';
import EditDomainDialog from './EditDomainDialog';
enum RefreshStatus {
IDLE,
@@ -24,16 +29,16 @@ enum RefreshStatus {
interface DomainCardProps {
domain: DomainDetails;
repo: RepositoryDetails;
project: ProjectDetails;
}
const CHECK_FAIL_TIMEOUT = 5000; // In milliseconds
const DomainCard = ({ domain }: DomainCardProps) => {
const DomainCard = ({ domain, repo, project }: DomainCardProps) => {
const [refreshStatus, SetRefreshStatus] = useState(RefreshStatus.IDLE);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const currProject = projectData.filter(
(data) => data.id === domain.projectid,
);
const [editDialogOpen, setEditDialogOpen] = useState(false);
return (
<>
@@ -69,10 +74,17 @@ const DomainCard = ({ domain }: DomainCardProps) => {
<button className="border-2 rounded-full w-8 h-8">...</button>
</MenuHandler>
<MenuList>
<MenuItem className="text-black">^ Edit domain</MenuItem>
<MenuItem
className="text-black"
onClick={() => {
setEditDialogOpen((preVal) => !preVal);
}}
>
^ Edit domain
</MenuItem>
<MenuItem
className="text-red-500"
onClick={() => setDeleteDialogOpen(true)}
onClick={() => setDeleteDialogOpen((preVal) => !preVal)}
>
^ Delete domain
</MenuItem>
@@ -94,7 +106,7 @@ const DomainCard = ({ domain }: DomainCardProps) => {
<Typography variant="small">
Once deleted, the project{' '}
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
{currProject[0].title}
{project.title}
</span>{' '}
will not be accessible from the domain{' '}
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
@@ -143,6 +155,14 @@ const DomainCard = ({ domain }: DomainCardProps) => {
</table>
</Card>
)}
<EditDomainDialog
handleOpen={() => {
setEditDialogOpen((preVal) => !preVal);
}}
open={editDialogOpen}
domain={domain}
repo={repo}
/>
</>
);
};
@@ -1,15 +1,25 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useParams, Link } from 'react-router-dom';
import { Button, Typography } from '@material-tailwind/react';
import DomainCard from './DomainCard';
import domainsData from '../../../../assets/domains.json';
import { DomainDetails } from '../../../../types/project';
import domainsData from '../../../../assets/domains.json';
import repositories from '../../../../assets/repositories.json';
import projectData from '../../../../assets/projects.json';
const Domains = () => {
const { id } = useParams();
const currProject = useMemo(() => {
return projectData.find((data) => data.id === Number(id));
}, [id]);
const linkedRepo = useMemo(() => {
return repositories.find((repo) => repo.id === currProject?.repositoryId);
}, [currProject]);
return (
<>
<div className="flex justify-between p-2">
@@ -21,13 +31,16 @@ const Domains = () => {
</Link>
</div>
{(domainsData as DomainDetails[])
.filter((domain) => {
return Number(id) == domain.projectid;
})
.map((domain) => {
return <DomainCard domain={domain} key={domain.id} />;
})}
{(domainsData as DomainDetails[]).map((domain) => {
return (
<DomainCard
domain={domain}
key={domain.id}
repo={linkedRepo!}
project={currProject!}
/>
);
})}
</>
);
};
@@ -0,0 +1,153 @@
import React, { useMemo } from 'react';
import { Controller, useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import {
Button,
Dialog,
DialogHeader,
DialogBody,
DialogFooter,
Input,
Typography,
Select,
Option,
} from '@material-tailwind/react';
import { DomainDetails, RepositoryDetails } from '../../../../types/project';
import domains from '../../../../assets/domains.json';
const DEFAULT_REDIRECT_OPTIONS = ['none'];
interface EditDomainDialogProp {
open: boolean;
handleOpen: () => void;
domain: DomainDetails;
repo: RepositoryDetails;
}
const EditDomainDialog = ({
open,
handleOpen,
domain,
repo,
}: EditDomainDialogProp) => {
const getRedirectUrl = (domain: DomainDetails) => {
const domainArr = domain.name.split('www.');
let redirectUrl = '';
if (domain.name.startsWith('www.')) {
redirectUrl = domainArr[1];
} else {
redirectUrl = `www.${domainArr[0]}`;
}
return redirectUrl;
};
const redirectOptions = useMemo(() => {
const redirectUrl = getRedirectUrl(domain);
return [...DEFAULT_REDIRECT_OPTIONS, redirectUrl];
}, [domain]);
const isDisableDropdown = useMemo(() => {
const redirectUrl = getRedirectUrl(domain);
const domainRedirected = domains.find(
(domain) => domain.name === redirectUrl,
);
return domainRedirected?.isRedirectedto;
}, [domain]);
const {
handleSubmit,
register,
control,
watch,
formState: { isValid, isDirty },
} = useForm({
defaultValues: {
name: domain.name,
branch: repo.branch[0],
redirectedTo: !domain.isRedirectedto
? redirectOptions[0]
: redirectOptions[1],
},
});
return (
<Dialog open={open} handler={handleOpen}>
<DialogHeader className="flex justify-between">
<div>Edit domain</div>
<Button
variant="outlined"
onClick={handleOpen}
className="mr-1 rounded-3xl"
>
X
</Button>
</DialogHeader>
<form
onSubmit={handleSubmit(() => {
handleOpen();
toast.success(`Domain ${domain.name} has been updated`);
})}
>
<DialogBody className="flex flex-col gap-2 p-4">
<Typography variant="small">Domain name</Typography>
<Input crossOrigin={undefined} {...register('name')} />
<Typography variant="small">Redirect to</Typography>
<Controller
name="redirectedTo"
control={control}
render={({ field }) => (
<Select {...field} disabled={isDisableDropdown}>
{redirectOptions.map((option, key) => (
<Option key={key} value={option}>
^ {option}
</Option>
))}
</Select>
)}
/>
{isDisableDropdown && (
<div className="flex p-2 gap-2 text-black bg-gray-300 rounded-lg">
<div>^</div>
<Typography variant="small">
Domain {redirectOptions[1]} redirects to this domain so you
can not redirect this doman further.
</Typography>
</div>
)}
<Typography variant="small">Git branch</Typography>
<Input
crossOrigin={undefined}
{...register('branch', {
validate: (value) => repo.branch.includes(value),
})}
disabled={watch('redirectedTo') !== DEFAULT_REDIRECT_OPTIONS[0]}
/>
{!isValid && (
<Typography variant="small" className="text-red-500">
We couldn&apos;t find this branch in the connected Git repository.
</Typography>
)}
</DialogBody>
<DialogFooter className="flex justify-start">
<Button variant="outlined" onClick={handleOpen} className="mr-1">
Cancel
</Button>
<Button
variant="gradient"
color="blue"
type="submit"
disabled={!isDirty}
>
Save changes
</Button>
</DialogFooter>
</form>
</Dialog>
);
};
export default EditDomainDialog;