forked from cerc-io/snowballtools-base
Set subdomain for project and set URL for each deployment (#52)
* Display current deployment info in overview tab * Add assign domain dialog box in deployments * Add empty link for project settings in assign domain dialog box * Use react router dom link * Add sub domain to project entity * Add deployment url with custom generated string * Set nano id to deployment id * Add sub domain while creating new project * Use same id as in url * Update readme steps for production build * Update README --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
"express": "^4.18.2",
|
||||
"fs-extra": "^11.2.0",
|
||||
"graphql": "^16.8.1",
|
||||
"nanoid": "3",
|
||||
"nanoid-dictionary": "^5.0.0-beta.1",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"toml": "^3.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
|
||||
@@ -4,3 +4,5 @@ export const DEFAULT_GQL_PATH = '/graphql';
|
||||
|
||||
// Note: temporary hardcoded user, later to be derived from auth token
|
||||
export const USER_ID = 1;
|
||||
|
||||
export const PROJECT_DOMAIN = 'snowball.xyz';
|
||||
|
||||
@@ -2,6 +2,8 @@ import { DataSource, DeepPartial } from 'typeorm';
|
||||
import path from 'path';
|
||||
import debug from 'debug';
|
||||
import assert from 'assert';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { lowercase, numbers } from 'nanoid-dictionary';
|
||||
|
||||
import { DatabaseConfig } from './config';
|
||||
import { User } from './entity/User';
|
||||
@@ -11,9 +13,12 @@ import { Deployment, Environment } from './entity/Deployment';
|
||||
import { Permission, ProjectMember } from './entity/ProjectMember';
|
||||
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
||||
import { Domain } from './entity/Domain';
|
||||
import { PROJECT_DOMAIN } from './constants';
|
||||
|
||||
const log = debug('snowball:database');
|
||||
|
||||
const nanoid = customAlphabet(lowercase + numbers, 8);
|
||||
|
||||
// TODO: Fix order of methods
|
||||
export class Database {
|
||||
private dataSource: DataSource;
|
||||
@@ -90,6 +95,7 @@ export class Database {
|
||||
const project = await projectRepository
|
||||
.createQueryBuilder('project')
|
||||
.leftJoinAndSelect('project.deployments', 'deployments', 'deployments.isCurrent = true')
|
||||
.leftJoinAndSelect('deployments.createdBy', 'user')
|
||||
.leftJoinAndSelect('deployments.domain', 'domain')
|
||||
.leftJoinAndSelect('project.owner', 'owner')
|
||||
.leftJoinAndSelect('project.organization', 'organization')
|
||||
@@ -314,7 +320,7 @@ export class Database {
|
||||
|
||||
async updateDeploymentById (deploymentId: string, updates: DeepPartial<Deployment>): Promise<boolean> {
|
||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
||||
const updateResult = await deploymentRepository.update({ id: Number(deploymentId) }, updates);
|
||||
const updateResult = await deploymentRepository.update({ id: deploymentId }, updates);
|
||||
|
||||
if (updateResult.affected) {
|
||||
return updateResult.affected > 0;
|
||||
@@ -341,6 +347,8 @@ export class Database {
|
||||
id: Number(projectDetails.organizationId)
|
||||
});
|
||||
|
||||
newProject.subDomain = `${newProject.name}.${PROJECT_DOMAIN}`;
|
||||
|
||||
return projectRepository.save(newProject);
|
||||
}
|
||||
|
||||
@@ -364,14 +372,14 @@ export class Database {
|
||||
createdBy: true
|
||||
},
|
||||
where: {
|
||||
id: Number(deploymentId)
|
||||
id: deploymentId
|
||||
}
|
||||
});
|
||||
|
||||
if (deployment === null) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
const { id, createdAt, updatedAt, ...updatedDeployment } = deployment;
|
||||
const { createdAt, updatedAt, ...updatedDeployment } = deployment;
|
||||
|
||||
if (updatedDeployment.environment === Environment.Production) {
|
||||
// TODO: Put isCurrent field in project
|
||||
@@ -381,7 +389,10 @@ export class Database {
|
||||
});
|
||||
}
|
||||
|
||||
await deploymentRepository.update({ id: Number(deploymentId) }, { domain: null, isCurrent: false });
|
||||
await deploymentRepository.update({ id: deploymentId }, { domain: null, isCurrent: false });
|
||||
|
||||
updatedDeployment.id = nanoid();
|
||||
updatedDeployment.url = `${updatedDeployment.id}-${updatedDeployment.project.subDomain}`;
|
||||
|
||||
return deploymentRepository.save(updatedDeployment);
|
||||
}
|
||||
@@ -442,7 +453,7 @@ export class Database {
|
||||
|
||||
const oldCurrentDeploymentUpdate = await deploymentRepository.update({ project: { id: projectId }, isCurrent: true }, { isCurrent: false, domain: null });
|
||||
|
||||
const newCurrentDeploymentUpdate = await deploymentRepository.update({ id: Number(deploymentId) }, { isCurrent: true, domain: oldCurrentDeployment?.domain });
|
||||
const newCurrentDeploymentUpdate = await deploymentRepository.update({ id: deploymentId }, { isCurrent: true, domain: oldCurrentDeployment?.domain });
|
||||
|
||||
if (oldCurrentDeploymentUpdate.affected && newCurrentDeploymentUpdate.affected) {
|
||||
return oldCurrentDeploymentUpdate.affected > 0 && newCurrentDeploymentUpdate.affected > 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
PrimaryColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
@@ -27,8 +27,9 @@ enum Status {
|
||||
|
||||
@Entity()
|
||||
export class Deployment {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
// TODO: set custom generated id
|
||||
@PrimaryColumn('varchar')
|
||||
id!: string;
|
||||
|
||||
@ManyToOne(() => Project, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'projectId' })
|
||||
@@ -47,6 +48,9 @@ export class Deployment {
|
||||
@Column('varchar')
|
||||
title!: string;
|
||||
|
||||
@Column('varchar')
|
||||
url!: string;
|
||||
|
||||
@Column({
|
||||
enum: Environment
|
||||
})
|
||||
|
||||
@@ -57,6 +57,9 @@ export class Project {
|
||||
@Column('varchar')
|
||||
icon!: string;
|
||||
|
||||
@Column('varchar')
|
||||
subDomain!: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt!: Date;
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ type Project {
|
||||
updatedAt: String!
|
||||
organization: Organization!
|
||||
icon: String
|
||||
subDomain: String
|
||||
}
|
||||
|
||||
type ProjectMember {
|
||||
@@ -89,6 +90,7 @@ type Deployment {
|
||||
branch: String!
|
||||
commitHash: String!
|
||||
title: String!
|
||||
url: String!
|
||||
environment: Environment!
|
||||
isCurrent: Boolean!
|
||||
status: DeploymentStatus!
|
||||
|
||||
@@ -52,6 +52,7 @@ export const deploymentToGqlType = (dbDeployment: Deployment): any => {
|
||||
branch: dbDeployment.branch,
|
||||
commitHash: dbDeployment.commitHash,
|
||||
title: dbDeployment.title,
|
||||
url: dbDeployment.url,
|
||||
environment: dbDeployment.environment,
|
||||
isCurrent: dbDeployment.isCurrent,
|
||||
status: dbDeployment.status,
|
||||
|
||||
+30
-10
@@ -3,110 +3,130 @@
|
||||
"projectIndex": 0,
|
||||
"domainIndex":0,
|
||||
"createdByIndex": 0,
|
||||
"id":"ffhae3zq",
|
||||
"title": "nextjs-boilerplate-1",
|
||||
"status": "Building",
|
||||
"environment": "Production",
|
||||
"isCurrent": true,
|
||||
"branch": "main",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "testProject-ffhae3zq.testProject.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 0,
|
||||
"domainIndex":1,
|
||||
"createdByIndex": 0,
|
||||
"id":"vehagei8",
|
||||
"title": "nextjs-boilerplate-2",
|
||||
"status": "Ready",
|
||||
"environment": "Preview",
|
||||
"isCurrent": false,
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "testProject-vehagei8.testProject.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 0,
|
||||
"domainIndex":2,
|
||||
"createdByIndex": 0,
|
||||
"id":"qmgekyte",
|
||||
"title": "nextjs-boilerplate-3",
|
||||
"status": "Error",
|
||||
"environment": "Development",
|
||||
"isCurrent": false,
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "testProject-qmgekyte.testProject.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 0,
|
||||
"domainIndex": null,
|
||||
"createdByIndex": 0,
|
||||
"id":"f8wsyim6",
|
||||
"title": "nextjs-boilerplate-4",
|
||||
"status": "Ready",
|
||||
"environment": "Production",
|
||||
"isCurrent": false,
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "testProject-f8wsyim6.testProject.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 1,
|
||||
"domainIndex":3,
|
||||
"createdByIndex": 1,
|
||||
"id":"eO8cckxk",
|
||||
"title": "nextjs-boilerplate-1",
|
||||
"status": "Building",
|
||||
"environment": "Production",
|
||||
"isCurrent": true,
|
||||
"branch": "main",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "testProject-2-eO8cckxk.testProject-2.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 1,
|
||||
"domainIndex":4,
|
||||
"createdByIndex": 1,
|
||||
"id":"yaq0t5yw",
|
||||
"title": "nextjs-boilerplate-2",
|
||||
"status": "Ready",
|
||||
"environment": "Preview",
|
||||
"isCurrent": false,
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "testProject-2-yaq0t5yw.testProject-2.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 1,
|
||||
"domainIndex":5,
|
||||
"createdByIndex": 1,
|
||||
"id":"hwwr6sbx",
|
||||
"title": "nextjs-boilerplate-3",
|
||||
"status": "Error",
|
||||
"environment": "Development",
|
||||
"isCurrent": false,
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "testProject-2-hwwr6sbx.testProject-2.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 2,
|
||||
"domainIndex":6,
|
||||
"createdByIndex": 2,
|
||||
"id":"ndxje48a",
|
||||
"title": "nextjs-boilerplate-1",
|
||||
"status": "Building",
|
||||
"environment": "Production",
|
||||
"isCurrent": true,
|
||||
"branch": "main",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "iglootools-ndxje48a.iglootools.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 2,
|
||||
"domainIndex":7,
|
||||
"createdByIndex": 2,
|
||||
"id":"gtgpgvei",
|
||||
"title": "nextjs-boilerplate-2",
|
||||
"status": "Ready",
|
||||
"environment": "Preview",
|
||||
"isCurrent": false,
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "iglootools-gtgpgvei.iglootools.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"projectIndex": 2,
|
||||
"domainIndex":8,
|
||||
"createdByIndex": 2,
|
||||
"id":"b4bpthjr",
|
||||
"title": "nextjs-boilerplate-3",
|
||||
"status": "Error",
|
||||
"environment": "Development",
|
||||
"isCurrent": false,
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz"
|
||||
"commitHash": "testXyz",
|
||||
"url": "iglootools-b4bpthjr.iglootools.snowball.xyz"
|
||||
}
|
||||
]
|
||||
|
||||
+10
-5
@@ -9,7 +9,8 @@
|
||||
"template": "test",
|
||||
"framework": "test",
|
||||
"webhooks": [],
|
||||
"icon": ""
|
||||
"icon": "",
|
||||
"subDomain": "testProject.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"ownerIndex": 1,
|
||||
@@ -21,7 +22,8 @@
|
||||
"template": "test-2",
|
||||
"framework": "test-2",
|
||||
"webhooks": [],
|
||||
"icon": ""
|
||||
"icon": "",
|
||||
"subDomain": "testProject-2.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"ownerIndex": 2,
|
||||
@@ -33,7 +35,8 @@
|
||||
"template": "test-3",
|
||||
"framework": "test-3",
|
||||
"webhooks": [],
|
||||
"icon": ""
|
||||
"icon": "",
|
||||
"subDomain": "iglootools.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"ownerIndex": 1,
|
||||
@@ -45,7 +48,8 @@
|
||||
"template": "test-4",
|
||||
"framework": "test-4",
|
||||
"webhooks": [],
|
||||
"icon": ""
|
||||
"icon": "",
|
||||
"subDomain": "iglootools-2.snowball.xyz"
|
||||
},
|
||||
{
|
||||
"ownerIndex": 0,
|
||||
@@ -57,6 +61,7 @@
|
||||
"template": "test-5",
|
||||
"framework": "test-5",
|
||||
"webhooks": [],
|
||||
"icon": ""
|
||||
"icon": "",
|
||||
"subDomain": "snowball-2.snowball.xyz"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -55,8 +55,6 @@ const loadAndSaveData = async <Entity extends ObjectLiteral>(entityType: EntityT
|
||||
};
|
||||
|
||||
const generateTestData = async (dataSource: DataSource) => {
|
||||
const domainRepository = await dataSource.getRepository(Domain);
|
||||
|
||||
const savedUsers = await loadAndSaveData(User, dataSource, path.resolve(__dirname, USER_DATA_PATH));
|
||||
const savedOrgs = await loadAndSaveData(Organization, dataSource, path.resolve(__dirname, ORGANIZATION_DATA_PATH));
|
||||
|
||||
@@ -67,6 +65,8 @@ const generateTestData = async (dataSource: DataSource) => {
|
||||
|
||||
const savedProjects = await loadAndSaveData(Project, dataSource, path.resolve(__dirname, PROJECT_DATA_PATH), projectRelations);
|
||||
|
||||
const domainRepository = dataSource.getRepository(Domain);
|
||||
|
||||
const domainPrimaryRelations = {
|
||||
project: savedProjects
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"luxon": "^3.4.4",
|
||||
"octokit": "^3.1.2",
|
||||
"react": "^18.2.0",
|
||||
"react-code-blocks": "^0.1.6",
|
||||
"react-day-picker": "^8.9.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropdown": "^1.11.0",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Project } from 'gql-client';
|
||||
|
||||
import { Button, Typography } from '@material-tailwind/react';
|
||||
|
||||
@@ -16,14 +17,14 @@ const DEFAULT_FILTER_VALUE: FilterValue = {
|
||||
status: StatusOptions.ALL_STATUS,
|
||||
};
|
||||
|
||||
const DeploymentsTabPanel = ({ projectId }: { projectId: string }) => {
|
||||
const DeploymentsTabPanel = ({ project }: { project: Project }) => {
|
||||
const client = useGQLClient();
|
||||
|
||||
const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE);
|
||||
const [deployments, setDeployments] = useState<DeploymentDetails[]>([]);
|
||||
|
||||
const fetchDeployments = async () => {
|
||||
const { deployments } = await client.getDeployments(projectId);
|
||||
const { deployments } = await client.getDeployments(project.id);
|
||||
const updatedDeployments = deployments.map((deployment) => {
|
||||
return {
|
||||
...deployment,
|
||||
@@ -91,7 +92,7 @@ const DeploymentsTabPanel = ({ projectId }: { projectId: string }) => {
|
||||
key={key}
|
||||
currentDeployment={currentDeployment!}
|
||||
onUpdate={onUpdateDeploymenToProd}
|
||||
projectId={projectId}
|
||||
project={project}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -23,8 +23,7 @@ const OverviewTabPanel = ({ project }: OverviewProps) => {
|
||||
<div className="grow">
|
||||
<Typography>{project.name}</Typography>
|
||||
<Typography variant="small" color="gray">
|
||||
{project.deployments[0]?.domain?.name ??
|
||||
'No Production Deployment'}
|
||||
{project.subDomain}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,22 +54,29 @@ const OverviewTabPanel = ({ project }: OverviewProps) => {
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Source</p>
|
||||
<p>{project.deployments[0]?.branch}</p>
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Deployment</p>
|
||||
<p className="text-blue-600">
|
||||
{project.deployments[0]?.domain?.name}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Created</p>
|
||||
<p>
|
||||
{relativeTimeMs(project.createdAt)} by ^ {project.owner.name}
|
||||
</p>
|
||||
</div>
|
||||
{project.deployments.length !== 0 ? (
|
||||
<>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Source</p>
|
||||
<p>{project.deployments[0]?.branch}</p>
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Deployment</p>
|
||||
<p className="text-blue-600">
|
||||
{project.deployments[0]?.domain?.name}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Created</p>
|
||||
<p>
|
||||
{relativeTimeMs(project.deployments[0].createdAt)} by ^{' '}
|
||||
{project.deployments[0].createdBy.name}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>No current deployment found</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-2 p-2">
|
||||
<div className="flex justify-between">
|
||||
|
||||
@@ -47,7 +47,7 @@ const ProjectTabs = ({ project, onUpdate }: ProjectTabsProps) => {
|
||||
<OverviewTabPanel project={project} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<DeploymentsTabPanel projectId={project.id} />
|
||||
<DeploymentsTabPanel project={project} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Database />
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { CopyBlock, atomOneLight } from 'react-code-blocks';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogHeader,
|
||||
DialogBody,
|
||||
DialogFooter,
|
||||
} from '@material-tailwind/react';
|
||||
|
||||
interface AssignDomainProps {
|
||||
open: boolean;
|
||||
handleOpen: () => void;
|
||||
}
|
||||
|
||||
const AssignDomainDialog = ({ open, handleOpen }: AssignDomainProps) => {
|
||||
return (
|
||||
<Dialog open={open} handler={handleOpen}>
|
||||
<DialogHeader>Assign Domain</DialogHeader>
|
||||
<DialogBody>
|
||||
In order to assign a domain to your production deployments, configure it
|
||||
in the{' '}
|
||||
{/* TODO: Navigate to settings tab panel after clicking on project settings */}
|
||||
<Link to="" className="text-light-blue-800 inline">
|
||||
project settings{' '}
|
||||
</Link>
|
||||
(recommended). If you want to assign to this specific deployment,
|
||||
however, you can do so using our command-line interface:
|
||||
<CopyBlock
|
||||
text="snowball alias <deployment> <domain>"
|
||||
language=""
|
||||
showLineNumbers={false}
|
||||
theme={atomOneLight}
|
||||
/>
|
||||
</DialogBody>
|
||||
<DialogFooter className="flex justify-start">
|
||||
<Button
|
||||
className="rounded-3xl"
|
||||
variant="gradient"
|
||||
color="blue"
|
||||
onClick={handleOpen}
|
||||
>
|
||||
<span>Okay</span>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssignDomainDialog;
|
||||
+16
-6
@@ -10,11 +10,12 @@ import {
|
||||
ChipProps,
|
||||
} from '@material-tailwind/react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Environment } from 'gql-client';
|
||||
import { Environment, Project } from 'gql-client';
|
||||
|
||||
import { relativeTimeMs } from '../../../../utils/time';
|
||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
||||
import DeploymentDialogBodyCard from './DeploymentDialogBodyCard';
|
||||
import AssignDomainDialog from './AssignDomainDialog';
|
||||
import { DeploymentDetails, Status } from '../../../../types/project';
|
||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
||||
|
||||
@@ -22,7 +23,7 @@ interface DeployDetailsCardProps {
|
||||
deployment: DeploymentDetails;
|
||||
currentDeployment: DeploymentDetails;
|
||||
onUpdate: () => Promise<void>;
|
||||
projectId: string;
|
||||
project: Project;
|
||||
}
|
||||
|
||||
const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = {
|
||||
@@ -35,13 +36,14 @@ const DeploymentDetailsCard = ({
|
||||
deployment,
|
||||
currentDeployment,
|
||||
onUpdate,
|
||||
projectId,
|
||||
project,
|
||||
}: DeployDetailsCardProps) => {
|
||||
const client = useGQLClient();
|
||||
|
||||
const [changeToProduction, setChangeToProduction] = useState(false);
|
||||
const [redeployToProduction, setRedeployToProduction] = useState(false);
|
||||
const [rollbackDeployment, setRollbackDeployment] = useState(false);
|
||||
const [assignDomainDialog, setAssignDomainDialog] = useState(false);
|
||||
|
||||
const updateDeployment = async () => {
|
||||
const isUpdated = await client.updateDeploymentToProd(deployment.id);
|
||||
@@ -65,7 +67,7 @@ const DeploymentDetailsCard = ({
|
||||
|
||||
const rollbackDeploymentHandler = async () => {
|
||||
const isRollbacked = await client.rollbackDeployment(
|
||||
projectId,
|
||||
project.id,
|
||||
deployment.id,
|
||||
);
|
||||
if (isRollbacked) {
|
||||
@@ -80,7 +82,7 @@ const DeploymentDetailsCard = ({
|
||||
<div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
|
||||
<div className="col-span-2">
|
||||
<div className="flex">
|
||||
<Typography className=" basis-3/4">{deployment.title}</Typography>
|
||||
<Typography className=" basis-3/4">{deployment.url}</Typography>
|
||||
<Chip
|
||||
value={deployment.status}
|
||||
color={STATUS_COLORS[deployment.status] ?? 'gray'}
|
||||
@@ -110,7 +112,11 @@ const DeploymentDetailsCard = ({
|
||||
</MenuHandler>
|
||||
<MenuList>
|
||||
<MenuItem>^ Visit</MenuItem>
|
||||
<MenuItem>^ Assign domain</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => setAssignDomainDialog(!assignDomainDialog)}
|
||||
>
|
||||
^ Assign domain
|
||||
</MenuItem>
|
||||
{!(deployment.environment === Environment.Production) && (
|
||||
<MenuItem
|
||||
onClick={() => setChangeToProduction(!changeToProduction)}
|
||||
@@ -235,6 +241,10 @@ const DeploymentDetailsCard = ({
|
||||
</Typography>
|
||||
</div>
|
||||
</ConfirmDialog>
|
||||
<AssignDomainDialog
|
||||
open={assignDomainDialog}
|
||||
handleOpen={() => setAssignDomainDialog(!assignDomainDialog)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ query ($projectId: String!) {
|
||||
repository
|
||||
webhooks
|
||||
icon
|
||||
subDomain
|
||||
organization {
|
||||
id
|
||||
name
|
||||
@@ -54,6 +55,10 @@ query ($projectId: String!) {
|
||||
id
|
||||
name
|
||||
}
|
||||
createdBy {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +77,7 @@ query ($organizationId: String!) {
|
||||
repository
|
||||
updatedAt
|
||||
icon
|
||||
subDomain
|
||||
deployments {
|
||||
id
|
||||
branch
|
||||
@@ -121,6 +127,7 @@ query ($projectId: String!) {
|
||||
branch
|
||||
commitHash
|
||||
title
|
||||
url
|
||||
environment
|
||||
isCurrent
|
||||
status
|
||||
|
||||
@@ -63,6 +63,7 @@ export type Deployment = {
|
||||
branch: string
|
||||
commitHash: string
|
||||
title: string
|
||||
url: string
|
||||
environment: Environment
|
||||
isCurrent: boolean
|
||||
status: DeploymentStatus
|
||||
@@ -131,6 +132,7 @@ export type Project = {
|
||||
updatedAt: string
|
||||
organization: Organization
|
||||
icon: string
|
||||
subDomain: string
|
||||
}
|
||||
|
||||
export type GetProjectMembersResponse = {
|
||||
|
||||
Reference in New Issue
Block a user