forked from cerc-io/snowballtools-base
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:
parent
2cb1feedcb
commit
66aa8fed4f
@ -4,7 +4,8 @@
|
|||||||
"projectid": 1,
|
"projectid": 1,
|
||||||
"name": "randomurl.snowballtools.xyz",
|
"name": "randomurl.snowballtools.xyz",
|
||||||
"status": "live",
|
"status": "live",
|
||||||
"record": null
|
"record": null,
|
||||||
|
"isRedirectedto": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@ -15,7 +16,8 @@
|
|||||||
"type": "A",
|
"type": "A",
|
||||||
"name": "@",
|
"name": "@",
|
||||||
"value": "56.49.19.21"
|
"value": "56.49.19.21"
|
||||||
}
|
},
|
||||||
|
"isRedirectedto": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
@ -26,6 +28,7 @@
|
|||||||
"type": "CNAME",
|
"type": "CNAME",
|
||||||
"name": "www",
|
"name": "www",
|
||||||
"value": "cname.snowballtools.xyz"
|
"value": "cname.snowballtools.xyz"
|
||||||
}
|
},
|
||||||
|
"isRedirectedto": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
"message": "subscription added",
|
"message": "subscription added",
|
||||||
"createdAt": "2023-12-11T04:20:00",
|
"createdAt": "2023-12-11T04:20:00",
|
||||||
"branch": "main"
|
"branch": "main"
|
||||||
}
|
},
|
||||||
|
"repositoryId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@ -33,7 +34,8 @@
|
|||||||
"message": "component updates",
|
"message": "component updates",
|
||||||
"createdAt": "2023-12-11T04:20:00",
|
"createdAt": "2023-12-11T04:20:00",
|
||||||
"branch": "staging"
|
"branch": "staging"
|
||||||
}
|
},
|
||||||
|
"repositoryId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
@ -51,7 +53,8 @@
|
|||||||
"message": "No repo connected",
|
"message": "No repo connected",
|
||||||
"createdAt": "2023-12-01T04:20:00",
|
"createdAt": "2023-12-01T04:20:00",
|
||||||
"branch": "main"
|
"branch": "main"
|
||||||
}
|
},
|
||||||
|
"repositoryId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
@ -69,7 +72,8 @@
|
|||||||
"message": "hello world",
|
"message": "hello world",
|
||||||
"createdAt": "2023-12-01T04:20:00",
|
"createdAt": "2023-12-01T04:20:00",
|
||||||
"branch": "main"
|
"branch": "main"
|
||||||
}
|
},
|
||||||
|
"repositoryId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
@ -87,7 +91,8 @@
|
|||||||
"message": "404 added",
|
"message": "404 added",
|
||||||
"createdAt": "2023-12-09T04:20:00",
|
"createdAt": "2023-12-09T04:20:00",
|
||||||
"branch": "main"
|
"branch": "main"
|
||||||
}
|
},
|
||||||
|
"repositoryId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 6,
|
"id": 6,
|
||||||
@ -105,6 +110,7 @@
|
|||||||
"message": "design system integrated",
|
"message": "design system integrated",
|
||||||
"createdAt": "2023-12-09T04:20:00",
|
"createdAt": "2023-12-09T04:20:00",
|
||||||
"branch": "prod"
|
"branch": "prod"
|
||||||
}
|
},
|
||||||
|
"repositoryId": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,34 +4,39 @@
|
|||||||
"title": "project-101",
|
"title": "project-101",
|
||||||
"updatedAt": "2023-12-21T08:30:00",
|
"updatedAt": "2023-12-21T08:30:00",
|
||||||
"user": "bob",
|
"user": "bob",
|
||||||
"private": false
|
"private": false,
|
||||||
|
"branch": ["main", "prod", "dev"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"title": "project-102",
|
"title": "project-102",
|
||||||
"updatedAt": "2023-12-21T08:30:00",
|
"updatedAt": "2023-12-21T08:30:00",
|
||||||
"user": "alice",
|
"user": "alice",
|
||||||
"private": true
|
"private": true,
|
||||||
|
"branch": ["main", "prod", "dev"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"title": "project-103",
|
"title": "project-103",
|
||||||
"updatedAt": "2023-12-21T04:20:00",
|
"updatedAt": "2023-12-21T04:20:00",
|
||||||
"user": "charlie",
|
"user": "charlie",
|
||||||
"private": false
|
"private": false,
|
||||||
|
"branch": ["main", "prod", "dev"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"title": "project-104",
|
"title": "project-104",
|
||||||
"updatedAt": "2023-12-21T04:27:00",
|
"updatedAt": "2023-12-21T04:27:00",
|
||||||
"user": "alice",
|
"user": "alice",
|
||||||
"private": false
|
"private": false,
|
||||||
|
"branch": ["main", "prod", "dev"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"title": "project-105",
|
"title": "project-105",
|
||||||
"updatedAt": "2023-12-21T04:41:00",
|
"updatedAt": "2023-12-21T04:41:00",
|
||||||
"user": "ivan",
|
"user": "ivan",
|
||||||
"private": false
|
"private": false,
|
||||||
|
"branch": ["main", "prod", "dev"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Chip,
|
Chip,
|
||||||
Typography,
|
Typography,
|
||||||
@ -11,9 +11,14 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
|
|
||||||
import { DomainDetails, DomainStatus } from '../../../../types/project';
|
import {
|
||||||
|
DomainDetails,
|
||||||
|
DomainStatus,
|
||||||
|
ProjectDetails,
|
||||||
|
RepositoryDetails,
|
||||||
|
} from '../../../../types/project';
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
||||||
import projectData from '../../../../assets/projects.json';
|
import EditDomainDialog from './EditDomainDialog';
|
||||||
|
|
||||||
enum RefreshStatus {
|
enum RefreshStatus {
|
||||||
IDLE,
|
IDLE,
|
||||||
@ -24,16 +29,16 @@ enum RefreshStatus {
|
|||||||
|
|
||||||
interface DomainCardProps {
|
interface DomainCardProps {
|
||||||
domain: DomainDetails;
|
domain: DomainDetails;
|
||||||
|
repo: RepositoryDetails;
|
||||||
|
project: ProjectDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHECK_FAIL_TIMEOUT = 5000; // In milliseconds
|
const CHECK_FAIL_TIMEOUT = 5000; // In milliseconds
|
||||||
|
|
||||||
const DomainCard = ({ domain }: DomainCardProps) => {
|
const DomainCard = ({ domain, repo, project }: DomainCardProps) => {
|
||||||
const [refreshStatus, SetRefreshStatus] = useState(RefreshStatus.IDLE);
|
const [refreshStatus, SetRefreshStatus] = useState(RefreshStatus.IDLE);
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const currProject = projectData.filter(
|
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
||||||
(data) => data.id === domain.projectid,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -69,10 +74,17 @@ const DomainCard = ({ domain }: DomainCardProps) => {
|
|||||||
<button className="border-2 rounded-full w-8 h-8">...</button>
|
<button className="border-2 rounded-full w-8 h-8">...</button>
|
||||||
</MenuHandler>
|
</MenuHandler>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem className="text-black">^ Edit domain</MenuItem>
|
<MenuItem
|
||||||
|
className="text-black"
|
||||||
|
onClick={() => {
|
||||||
|
setEditDialogOpen((preVal) => !preVal);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
^ Edit domain
|
||||||
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className="text-red-500"
|
className="text-red-500"
|
||||||
onClick={() => setDeleteDialogOpen(true)}
|
onClick={() => setDeleteDialogOpen((preVal) => !preVal)}
|
||||||
>
|
>
|
||||||
^ Delete domain
|
^ Delete domain
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -94,7 +106,7 @@ const DomainCard = ({ domain }: DomainCardProps) => {
|
|||||||
<Typography variant="small">
|
<Typography variant="small">
|
||||||
Once deleted, the project{' '}
|
Once deleted, the project{' '}
|
||||||
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
|
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
|
||||||
{currProject[0].title}
|
{project.title}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
will not be accessible from the domain{' '}
|
will not be accessible from the domain{' '}
|
||||||
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
|
<span className="bg-blue-100 rounded-sm p-0.5 text-blue-700">
|
||||||
@ -143,6 +155,14 @@ const DomainCard = ({ domain }: DomainCardProps) => {
|
|||||||
</table>
|
</table>
|
||||||
</Card>
|
</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 { useParams, Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { Button, Typography } from '@material-tailwind/react';
|
import { Button, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
import DomainCard from './DomainCard';
|
import DomainCard from './DomainCard';
|
||||||
import domainsData from '../../../../assets/domains.json';
|
|
||||||
import { DomainDetails } from '../../../../types/project';
|
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 Domains = () => {
|
||||||
const { id } = useParams();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex justify-between p-2">
|
<div className="flex justify-between p-2">
|
||||||
@ -21,13 +31,16 @@ const Domains = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(domainsData as DomainDetails[])
|
{(domainsData as DomainDetails[]).map((domain) => {
|
||||||
.filter((domain) => {
|
return (
|
||||||
return Number(id) == domain.projectid;
|
<DomainCard
|
||||||
})
|
domain={domain}
|
||||||
.map((domain) => {
|
key={domain.id}
|
||||||
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'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;
|
@ -15,6 +15,7 @@ export interface ProjectDetails {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
branch: string;
|
branch: string;
|
||||||
};
|
};
|
||||||
|
repositoryId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeploymentDetails {
|
export interface DeploymentDetails {
|
||||||
@ -54,6 +55,7 @@ export interface RepositoryDetails {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
user: string;
|
user: string;
|
||||||
private: boolean;
|
private: boolean;
|
||||||
|
branch: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum GitSelect {
|
export enum GitSelect {
|
||||||
@ -77,4 +79,5 @@ export interface DomainDetails {
|
|||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
isRedirectedto: boolean;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user