Implement routes for project settings tab (#64)
* Add routes to settings tab panel * Refactor code to move settings tab components to pages * Rename registry fields in project and deployment entity * Use kebab case for routes --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
parent
559e0f8934
commit
76dfd3bb76
@ -63,10 +63,10 @@ export class Deployment {
|
|||||||
url!: string;
|
url!: string;
|
||||||
|
|
||||||
@Column('varchar')
|
@Column('varchar')
|
||||||
recordId!: string;
|
registryRecordId!: string;
|
||||||
|
|
||||||
@Column('simple-json')
|
@Column('simple-json')
|
||||||
recordData!: ApplicationRecord;
|
registryRecordData!: ApplicationRecord;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
enum: Environment
|
enum: Environment
|
||||||
|
@ -56,10 +56,10 @@ export class Project {
|
|||||||
prodBranch!: string;
|
prodBranch!: string;
|
||||||
|
|
||||||
@Column('varchar', { nullable: true })
|
@Column('varchar', { nullable: true })
|
||||||
recordId!: string | null;
|
registryRecordId!: string | null;
|
||||||
|
|
||||||
@Column('simple-json', { nullable: true })
|
@Column('simple-json', { nullable: true })
|
||||||
recordData!: ApplicationDeploymentRequest | null;
|
registryRecordData!: ApplicationDeploymentRequest | null;
|
||||||
|
|
||||||
@Column('text', { default: '' })
|
@Column('text', { default: '' })
|
||||||
description!: string;
|
description!: string;
|
||||||
|
@ -13,6 +13,7 @@ const APP_RECORD_TYPE = 'ApplicationRecord';
|
|||||||
const DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRequest';
|
const DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRequest';
|
||||||
const AUTHORITY_NAME = 'snowball';
|
const AUTHORITY_NAME = 'snowball';
|
||||||
|
|
||||||
|
// TODO: Move registry code to laconic-sdk/watcher-ts
|
||||||
export class Registry {
|
export class Registry {
|
||||||
private registry: LaconicRegistry;
|
private registry: LaconicRegistry;
|
||||||
private registryConfig: RegistryConfig;
|
private registryConfig: RegistryConfig;
|
||||||
@ -22,7 +23,7 @@ export class Registry {
|
|||||||
this.registry = new LaconicRegistry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId);
|
this.registry = new LaconicRegistry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createApplicationRecord (data: { recordName: string, appType: string }): Promise<{recordId: string, recordData: ApplicationRecord}> {
|
async createApplicationRecord (data: { recordName: string, appType: string }): Promise<{registryRecordId: string, registryRecordData: ApplicationRecord}> {
|
||||||
// TODO: Get record name from repo package.json name
|
// TODO: Get record name from repo package.json name
|
||||||
const recordName = data.recordName;
|
const recordName = data.recordName;
|
||||||
|
|
||||||
@ -77,10 +78,10 @@ export class Registry {
|
|||||||
await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.app_version}` }, this.registryConfig.privateKey, this.registryConfig.fee);
|
await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.app_version}` }, this.registryConfig.privateKey, this.registryConfig.fee);
|
||||||
await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.repository_ref}` }, this.registryConfig.privateKey, this.registryConfig.fee);
|
await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.repository_ref}` }, this.registryConfig.privateKey, this.registryConfig.fee);
|
||||||
|
|
||||||
return { recordId: result.data.id, recordData: applicationRecord };
|
return { registryRecordId: result.data.id, registryRecordData: applicationRecord };
|
||||||
}
|
}
|
||||||
|
|
||||||
async createApplicationDeploymentRequest (data: { appName: string }): Promise<{recordId: string, recordData: ApplicationDeploymentRequest}> {
|
async createApplicationDeploymentRequest (data: { appName: string }): Promise<{registryRecordId: string, registryRecordData: ApplicationDeploymentRequest}> {
|
||||||
const crn = this.getCrn(data.appName);
|
const crn = this.getCrn(data.appName);
|
||||||
const records = await this.registry.resolveNames([crn]);
|
const records = await this.registry.resolveNames([crn]);
|
||||||
const applicationRecord = records[0];
|
const applicationRecord = records[0];
|
||||||
@ -124,7 +125,7 @@ export class Registry {
|
|||||||
log(`Application deployment request record published: ${result.data.id}`);
|
log(`Application deployment request record published: ${result.data.id}`);
|
||||||
log('Application deployment request data:', applicationDeploymentRequest);
|
log('Application deployment request data:', applicationDeploymentRequest);
|
||||||
|
|
||||||
return { recordId: result.data.id, recordData: applicationDeploymentRequest };
|
return { registryRecordId: result.data.id, registryRecordData: applicationDeploymentRequest };
|
||||||
}
|
}
|
||||||
|
|
||||||
getCrn (appName: string): string {
|
getCrn (appName: string): string {
|
||||||
|
@ -214,7 +214,7 @@ export class Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createDeployment (userId: string, data: DeepPartial<Deployment>): Promise<Deployment> {
|
async createDeployment (userId: string, data: DeepPartial<Deployment>): Promise<Deployment> {
|
||||||
const { recordId, recordData } = await this.registry.createApplicationRecord({
|
const { registryRecordId, registryRecordData } = await this.registry.createApplicationRecord({
|
||||||
recordName: data.project?.name ?? '',
|
recordName: data.project?.name ?? '',
|
||||||
appType: data.project?.template ?? ''
|
appType: data.project?.template ?? ''
|
||||||
});
|
});
|
||||||
@ -226,15 +226,15 @@ export class Service {
|
|||||||
environment: data.environment,
|
environment: data.environment,
|
||||||
isCurrent: data.isCurrent,
|
isCurrent: data.isCurrent,
|
||||||
status: DeploymentStatus.Building,
|
status: DeploymentStatus.Building,
|
||||||
recordId,
|
registryRecordId,
|
||||||
recordData,
|
registryRecordData,
|
||||||
domain: data.domain,
|
domain: data.domain,
|
||||||
createdBy: Object.assign(new User(), {
|
createdBy: Object.assign(new User(), {
|
||||||
id: userId
|
id: userId
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
log(`Application record ${recordId} published for deployment ${newDeployement.id}`);
|
log(`Application record ${registryRecordId} published for deployment ${newDeployement.id}`);
|
||||||
return newDeployement;
|
return newDeployement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,10 +262,10 @@ export class Service {
|
|||||||
domain: null
|
domain: null
|
||||||
});
|
});
|
||||||
|
|
||||||
const { recordId, recordData } = await this.registry.createApplicationDeploymentRequest({ appName: project.name });
|
const { registryRecordId, registryRecordData } = await this.registry.createApplicationDeploymentRequest({ appName: project.name });
|
||||||
await this.db.updateProjectById(project.id, {
|
await this.db.updateProjectById(project.id, {
|
||||||
recordId,
|
registryRecordId,
|
||||||
recordData
|
registryRecordData
|
||||||
});
|
});
|
||||||
|
|
||||||
return project;
|
return project;
|
||||||
|
40
packages/backend/test/fixtures/deployments.json
vendored
40
packages/backend/test/fixtures/deployments.json
vendored
@ -7,8 +7,8 @@
|
|||||||
"status": "Building",
|
"status": "Building",
|
||||||
"environment": "Production",
|
"environment": "Production",
|
||||||
"isCurrent": true,
|
"isCurrent": true,
|
||||||
"recordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "testProject-ffhae3zq.snowball.xyz"
|
"url": "testProject-ffhae3zq.snowball.xyz"
|
||||||
@ -21,8 +21,8 @@
|
|||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"environment": "Preview",
|
"environment": "Preview",
|
||||||
"isCurrent": false,
|
"isCurrent": false,
|
||||||
"recordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "test",
|
"branch": "test",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "testProject-vehagei8.snowball.xyz"
|
"url": "testProject-vehagei8.snowball.xyz"
|
||||||
@ -35,8 +35,8 @@
|
|||||||
"status": "Error",
|
"status": "Error",
|
||||||
"environment": "Development",
|
"environment": "Development",
|
||||||
"isCurrent": false,
|
"isCurrent": false,
|
||||||
"recordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "test",
|
"branch": "test",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "testProject-qmgekyte.snowball.xyz"
|
"url": "testProject-qmgekyte.snowball.xyz"
|
||||||
@ -49,8 +49,8 @@
|
|||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"environment": "Production",
|
"environment": "Production",
|
||||||
"isCurrent": false,
|
"isCurrent": false,
|
||||||
"recordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "testProject-f8wsyim6.snowball.xyz"
|
"url": "testProject-f8wsyim6.snowball.xyz"
|
||||||
@ -63,8 +63,8 @@
|
|||||||
"status": "Building",
|
"status": "Building",
|
||||||
"environment": "Production",
|
"environment": "Production",
|
||||||
"isCurrent": true,
|
"isCurrent": true,
|
||||||
"recordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "testProject-2-eO8cckxk.snowball.xyz"
|
"url": "testProject-2-eO8cckxk.snowball.xyz"
|
||||||
@ -77,8 +77,8 @@
|
|||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"environment": "Preview",
|
"environment": "Preview",
|
||||||
"isCurrent": false,
|
"isCurrent": false,
|
||||||
"recordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "test",
|
"branch": "test",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "testProject-2-yaq0t5yw.snowball.xyz"
|
"url": "testProject-2-yaq0t5yw.snowball.xyz"
|
||||||
@ -91,8 +91,8 @@
|
|||||||
"status": "Error",
|
"status": "Error",
|
||||||
"environment": "Development",
|
"environment": "Development",
|
||||||
"isCurrent": false,
|
"isCurrent": false,
|
||||||
"recordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi",
|
"registryRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "test",
|
"branch": "test",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "testProject-2-hwwr6sbx.snowball.xyz"
|
"url": "testProject-2-hwwr6sbx.snowball.xyz"
|
||||||
@ -105,8 +105,8 @@
|
|||||||
"status": "Building",
|
"status": "Building",
|
||||||
"environment": "Production",
|
"environment": "Production",
|
||||||
"isCurrent": true,
|
"isCurrent": true,
|
||||||
"recordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "iglootools-ndxje48a.snowball.xyz"
|
"url": "iglootools-ndxje48a.snowball.xyz"
|
||||||
@ -119,8 +119,8 @@
|
|||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"environment": "Preview",
|
"environment": "Preview",
|
||||||
"isCurrent": false,
|
"isCurrent": false,
|
||||||
"recordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "test",
|
"branch": "test",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "iglootools-gtgpgvei.snowball.xyz"
|
"url": "iglootools-gtgpgvei.snowball.xyz"
|
||||||
@ -133,8 +133,8 @@
|
|||||||
"status": "Error",
|
"status": "Error",
|
||||||
"environment": "Development",
|
"environment": "Development",
|
||||||
"isCurrent": false,
|
"isCurrent": false,
|
||||||
"recordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
|
"registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"branch": "test",
|
"branch": "test",
|
||||||
"commitHash": "testXyz",
|
"commitHash": "testXyz",
|
||||||
"url": "iglootools-b4bpthjr.snowball.xyz"
|
"url": "iglootools-b4bpthjr.snowball.xyz"
|
||||||
|
20
packages/backend/test/fixtures/projects.json
vendored
20
packages/backend/test/fixtures/projects.json
vendored
@ -10,8 +10,8 @@
|
|||||||
"framework": "test",
|
"framework": "test",
|
||||||
"webhooks": [],
|
"webhooks": [],
|
||||||
"icon": "",
|
"icon": "",
|
||||||
"recordId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"subDomain": "testProject.snowball.xyz"
|
"subDomain": "testProject.snowball.xyz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -25,8 +25,8 @@
|
|||||||
"framework": "test-2",
|
"framework": "test-2",
|
||||||
"webhooks": [],
|
"webhooks": [],
|
||||||
"icon": "",
|
"icon": "",
|
||||||
"recordId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"subDomain": "testProject-2.snowball.xyz"
|
"subDomain": "testProject-2.snowball.xyz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -40,8 +40,8 @@
|
|||||||
"framework": "test-3",
|
"framework": "test-3",
|
||||||
"webhooks": [],
|
"webhooks": [],
|
||||||
"icon": "",
|
"icon": "",
|
||||||
"recordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"subDomain": "iglootools.snowball.xyz"
|
"subDomain": "iglootools.snowball.xyz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -55,8 +55,8 @@
|
|||||||
"framework": "test-4",
|
"framework": "test-4",
|
||||||
"webhooks": [],
|
"webhooks": [],
|
||||||
"icon": "",
|
"icon": "",
|
||||||
"recordId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"subDomain": "iglootools-2.snowball.xyz"
|
"subDomain": "iglootools-2.snowball.xyz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -70,8 +70,8 @@
|
|||||||
"framework": "test-5",
|
"framework": "test-5",
|
||||||
"webhooks": [],
|
"webhooks": [],
|
||||||
"icon": "",
|
"icon": "",
|
||||||
"recordId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
"registryRecordId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||||
"recordData": {},
|
"registryRecordData": {},
|
||||||
"subDomain": "snowball-2.snowball.xyz"
|
"subDomain": "snowball-2.snowball.xyz"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -3,7 +3,7 @@ import { UseFormRegister } from 'react-hook-form';
|
|||||||
|
|
||||||
import { Typography, Input, IconButton } from '@material-tailwind/react';
|
import { Typography, Input, IconButton } from '@material-tailwind/react';
|
||||||
|
|
||||||
import { EnvironmentVariablesFormValues } from './EnvironmentVariablesTabPanel';
|
import { EnvironmentVariablesFormValues } from '../../../../types/project';
|
||||||
|
|
||||||
interface AddEnvironmentVariableRowProps {
|
interface AddEnvironmentVariableRowProps {
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { Link, Outlet, useLocation, useOutletContext } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { Tabs, TabsHeader, TabsBody, Tab } from '@material-tailwind/react';
|
||||||
|
|
||||||
|
import { OutletContextType } from '../../../../types/project';
|
||||||
|
|
||||||
|
const tabsData = [
|
||||||
|
{
|
||||||
|
label: 'General',
|
||||||
|
icon: '^',
|
||||||
|
value: 'general',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Domains',
|
||||||
|
icon: '^',
|
||||||
|
value: 'domains',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Git',
|
||||||
|
icon: '^',
|
||||||
|
value: 'git',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Environment variables',
|
||||||
|
icon: '^',
|
||||||
|
value: 'environment-variables',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Members',
|
||||||
|
icon: '^',
|
||||||
|
value: 'members',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const SettingsTabPanel = () => {
|
||||||
|
const { project, onUpdate } = useOutletContext<OutletContextType>();
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const currentTab = useMemo(() => {
|
||||||
|
if (project) {
|
||||||
|
const currTabArr = location.pathname.split('settings');
|
||||||
|
return currTabArr[currTabArr.length - 1];
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [location, project]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tabs
|
||||||
|
value={currentTab}
|
||||||
|
orientation="vertical"
|
||||||
|
className="grid grid-cols-4"
|
||||||
|
>
|
||||||
|
<TabsHeader
|
||||||
|
className="bg-transparent col-span-1"
|
||||||
|
indicatorProps={{
|
||||||
|
className: 'bg-gray-900/10 shadow-none !text-gray-900',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabsData.map(({ label, value, icon }) => (
|
||||||
|
<Link key={value} to={value === 'general' ? '' : value}>
|
||||||
|
<Tab
|
||||||
|
value={value === 'general' ? '' : `/${value}`}
|
||||||
|
className="flex justify-start"
|
||||||
|
>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div>{icon}</div>
|
||||||
|
<div>{label}</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</TabsHeader>
|
||||||
|
<TabsBody className="col-span-2">
|
||||||
|
<Outlet context={{ project, onUpdate }} />
|
||||||
|
</TabsBody>
|
||||||
|
</Tabs>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingsTabPanel;
|
@ -1,92 +0,0 @@
|
|||||||
import React, { createElement } from 'react';
|
|
||||||
import { useOutletContext } from 'react-router-dom';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Tabs,
|
|
||||||
TabsHeader,
|
|
||||||
TabsBody,
|
|
||||||
Tab,
|
|
||||||
TabPanel,
|
|
||||||
} from '@material-tailwind/react';
|
|
||||||
|
|
||||||
import Domains from '../../../../components/projects/project/settings/Domains';
|
|
||||||
import GeneralTabPanel from '../../../../components/projects/project/settings/GeneralTabPanel';
|
|
||||||
import { EnvironmentVariablesTabPanel } from '../../../../components/projects/project/settings/EnvironmentVariablesTabPanel';
|
|
||||||
import GitTabPanel from '../../../../components/projects/project/settings/GitTabPanel';
|
|
||||||
import MembersTabPanel from '../../../../components/projects/project/settings/MembersTabPanel';
|
|
||||||
import { OutletContextType } from '../../../../types/project';
|
|
||||||
|
|
||||||
const tabsData = [
|
|
||||||
{
|
|
||||||
label: 'General',
|
|
||||||
icon: '^',
|
|
||||||
value: 'general',
|
|
||||||
component: GeneralTabPanel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Domains',
|
|
||||||
icon: '^',
|
|
||||||
value: 'domains',
|
|
||||||
component: Domains,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Git',
|
|
||||||
icon: '^',
|
|
||||||
value: 'git',
|
|
||||||
component: GitTabPanel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Environment variables',
|
|
||||||
icon: '^',
|
|
||||||
value: 'environmentVariables',
|
|
||||||
component: EnvironmentVariablesTabPanel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Members',
|
|
||||||
icon: '^',
|
|
||||||
value: 'members',
|
|
||||||
component: MembersTabPanel,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const SettingsTabPanel = () => {
|
|
||||||
const { project, onUpdate } = useOutletContext<OutletContextType>();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Tabs
|
|
||||||
value={'general'}
|
|
||||||
orientation="vertical"
|
|
||||||
className="grid grid-cols-4"
|
|
||||||
>
|
|
||||||
<TabsHeader
|
|
||||||
className="bg-transparent col-span-1"
|
|
||||||
indicatorProps={{
|
|
||||||
className: 'bg-gray-900/10 shadow-none !text-gray-900',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{tabsData.map(({ label, value, icon }) => (
|
|
||||||
<Tab key={value} value={value} className="flex justify-start">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<div>{icon}</div>
|
|
||||||
<div>{label}</div>
|
|
||||||
</div>
|
|
||||||
</Tab>
|
|
||||||
))}
|
|
||||||
</TabsHeader>
|
|
||||||
<TabsBody className="col-span-2">
|
|
||||||
{tabsData.map(({ value, component }) => (
|
|
||||||
<TabPanel key={value} value={value} className="p-2">
|
|
||||||
{createElement(component, {
|
|
||||||
project: project,
|
|
||||||
onUpdate: onUpdate,
|
|
||||||
})}
|
|
||||||
</TabPanel>
|
|
||||||
))}
|
|
||||||
</TabsBody>
|
|
||||||
</Tabs>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SettingsTabPanel;
|
|
@ -1,8 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import OverviewTabPanel from './OverviewTabPanel';
|
import OverviewTabPanel from './Overview';
|
||||||
import DeploymentsTabPanel from './DeploymentsTabPanel';
|
import DeploymentsTabPanel from './Deployments';
|
||||||
import SettingsTabPanel from './SettingsTabPanel';
|
import SettingsTabPanel from './Settings';
|
||||||
|
import GeneralTabPanel from './settings/General';
|
||||||
|
import GitTabPanel from './settings/Git';
|
||||||
|
import { EnvironmentVariablesTabPanel } from './settings/EnvironmentVariables';
|
||||||
|
import MembersTabPanel from './settings/Members';
|
||||||
|
import Domains from './settings/Domains';
|
||||||
|
|
||||||
const Database = () => (
|
const Database = () => (
|
||||||
<div>
|
<div>
|
||||||
@ -23,6 +28,29 @@ const Integrations = () => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const settingsTabRoutes = [
|
||||||
|
{
|
||||||
|
index: true,
|
||||||
|
element: <GeneralTabPanel />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'domains',
|
||||||
|
element: <Domains />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'git',
|
||||||
|
element: <GitTabPanel />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'environment-variables',
|
||||||
|
element: <EnvironmentVariablesTabPanel />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'members',
|
||||||
|
element: <MembersTabPanel />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export const projectTabRoutes = [
|
export const projectTabRoutes = [
|
||||||
{
|
{
|
||||||
index: true,
|
index: true,
|
||||||
@ -43,5 +71,6 @@ export const projectTabRoutes = [
|
|||||||
{
|
{
|
||||||
path: 'settings',
|
path: 'settings',
|
||||||
element: <SettingsTabPanel />,
|
element: <SettingsTabPanel />,
|
||||||
|
children: settingsTabRoutes,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useOutletContext } from 'react-router-dom';
|
||||||
import { Domain, Project } from 'gql-client';
|
import { Domain } from 'gql-client';
|
||||||
|
|
||||||
import { Button, Typography } from '@material-tailwind/react';
|
import { Button, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
import DomainCard from './DomainCard';
|
import DomainCard from '../../../../../components/projects/project/settings/DomainCard';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
||||||
import repositories from '../../../../assets/repositories.json';
|
import repositories from '../../../../../assets/repositories.json';
|
||||||
|
import { OutletContextType } from '../../../../../types/project';
|
||||||
|
|
||||||
const Domains = ({ project }: { project: Project }) => {
|
const Domains = () => {
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
|
const { project } = useOutletContext<OutletContextType>();
|
||||||
|
|
||||||
const [domains, setDomains] = useState<Domain[]>([]);
|
const [domains, setDomains] = useState<Domain[]>([]);
|
||||||
|
|
||||||
const fetchDomains = async () => {
|
const fetchDomains = async () => {
|
@ -13,22 +13,11 @@ import {
|
|||||||
Chip,
|
Chip,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
|
|
||||||
import AddEnvironmentVariableRow from './AddEnvironmentVariableRow';
|
import AddEnvironmentVariableRow from '../../../../../components/projects/project/settings/AddEnvironmentVariableRow';
|
||||||
import DisplayEnvironmentVariables from './DisplayEnvironmentVariables';
|
import DisplayEnvironmentVariables from '../../../../../components/projects/project/settings/DisplayEnvironmentVariables';
|
||||||
import HorizontalLine from '../../../HorizontalLine';
|
import HorizontalLine from '../../../../../components/HorizontalLine';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
||||||
|
import { EnvironmentVariablesFormValues } from '../../../../../types/project';
|
||||||
export type EnvironmentVariablesFormValues = {
|
|
||||||
variables: {
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
}[];
|
|
||||||
environment: {
|
|
||||||
development: boolean;
|
|
||||||
preview: boolean;
|
|
||||||
production: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EnvironmentVariablesTabPanel = () => {
|
export const EnvironmentVariablesTabPanel = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
@ -1,15 +1,16 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useOutletContext } from 'react-router-dom';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { Organization, Project } from 'gql-client';
|
import { Organization } from 'gql-client';
|
||||||
|
|
||||||
import { Button, Typography, Input, Option } from '@material-tailwind/react';
|
import { Button, Typography, Input, Option } from '@material-tailwind/react';
|
||||||
|
|
||||||
import DeleteProjectDialog from './DeleteProjectDialog';
|
import DeleteProjectDialog from '../../../../../components/projects/project/settings/DeleteProjectDialog';
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import ConfirmDialog from '../../../../../components/shared/ConfirmDialog';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
||||||
import AsyncSelect from '../../../shared/AsyncSelect';
|
import AsyncSelect from '../../../../../components/shared/AsyncSelect';
|
||||||
|
import { OutletContextType } from '../../../../../types/project';
|
||||||
|
|
||||||
const CopyIcon = ({ value }: { value: string }) => {
|
const CopyIcon = ({ value }: { value: string }) => {
|
||||||
return (
|
return (
|
||||||
@ -25,14 +26,10 @@ const CopyIcon = ({ value }: { value: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GeneralTabPanel = ({
|
const GeneralTabPanel = () => {
|
||||||
project,
|
|
||||||
onUpdate,
|
|
||||||
}: {
|
|
||||||
project: Project;
|
|
||||||
onUpdate: () => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
|
const { project, onUpdate } = useOutletContext<OutletContextType>();
|
||||||
|
|
||||||
const [transferOrganizations, setTransferOrganizations] = useState<
|
const [transferOrganizations, setTransferOrganizations] = useState<
|
||||||
Organization[]
|
Organization[]
|
||||||
>([]);
|
>([]);
|
@ -1,12 +1,13 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { Project } from 'gql-client';
|
|
||||||
|
|
||||||
import { Button, Input, Switch, Typography } from '@material-tailwind/react';
|
import { Button, Input, Switch, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
import WebhookCard from './WebhookCard';
|
import WebhookCard from '../../../../../components/projects/project/settings/WebhookCard';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
||||||
|
import { OutletContextType } from '../../../../../types/project';
|
||||||
|
|
||||||
type UpdateProdBranchValues = {
|
type UpdateProdBranchValues = {
|
||||||
prodBranch: string;
|
prodBranch: string;
|
||||||
@ -16,14 +17,9 @@ type UpdateWebhooksValues = {
|
|||||||
webhookUrl: string;
|
webhookUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GitTabPanel = ({
|
const GitTabPanel = () => {
|
||||||
project,
|
|
||||||
onUpdate,
|
|
||||||
}: {
|
|
||||||
project: Project;
|
|
||||||
onUpdate: () => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
|
const { project, onUpdate } = useOutletContext<OutletContextType>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register: registerProdBranch,
|
register: registerProdBranch,
|
@ -1,22 +1,20 @@
|
|||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import toast, { Toaster } from 'react-hot-toast';
|
import { useOutletContext } from 'react-router-dom';
|
||||||
import {
|
import toast from 'react-hot-toast';
|
||||||
Permission,
|
import { Permission, AddProjectMemberInput, ProjectMember } from 'gql-client';
|
||||||
Project,
|
|
||||||
AddProjectMemberInput,
|
|
||||||
ProjectMember,
|
|
||||||
} from 'gql-client';
|
|
||||||
|
|
||||||
import { Chip, Button, Typography } from '@material-tailwind/react';
|
import { Chip, Button, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
import MemberCard from './MemberCard';
|
import MemberCard from '../../../../../components/projects/project/settings/MemberCard';
|
||||||
import AddMemberDialog from './AddMemberDialog';
|
import AddMemberDialog from '../../../../../components/projects/project/settings/AddMemberDialog';
|
||||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
||||||
|
import { OutletContextType } from '../../../../../types/project';
|
||||||
|
|
||||||
const FIRST_MEMBER_CARD = 0;
|
const FIRST_MEMBER_CARD = 0;
|
||||||
|
|
||||||
const MembersTabPanel = ({ project }: { project: Project }) => {
|
const MembersTabPanel = () => {
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
|
const { project } = useOutletContext<OutletContextType>();
|
||||||
|
|
||||||
const [addmemberDialogOpen, setAddMemberDialogOpen] = useState(false);
|
const [addmemberDialogOpen, setAddMemberDialogOpen] = useState(false);
|
||||||
|
|
||||||
@ -127,7 +125,6 @@ const MembersTabPanel = ({ project }: { project: Project }) => {
|
|||||||
open={addmemberDialogOpen}
|
open={addmemberDialogOpen}
|
||||||
handleAddMember={addMemberHandler}
|
handleAddMember={addMemberHandler}
|
||||||
/>
|
/>
|
||||||
<Toaster />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -65,3 +65,15 @@ export type OutletContextType = {
|
|||||||
project: Project;
|
project: Project;
|
||||||
onUpdate: () => Promise<void>;
|
onUpdate: () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EnvironmentVariablesFormValues = {
|
||||||
|
variables: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
environment: {
|
||||||
|
development: boolean;
|
||||||
|
preview: boolean;
|
||||||
|
production: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user