Skip to content

Commit

Permalink
feat: deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
HuberTRoy committed Dec 13, 2023
1 parent a9aaf87 commit 60f49ea
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 24 deletions.
84 changes: 80 additions & 4 deletions src/components/ProjectDeployments/ProjectDeployments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { Markdown } from '@subql/components';
import { useCreateDeployment } from '@hooks';
import { Markdown, Typography } from '@subql/components';
import { Form, Modal, Radio } from 'antd';
import { useForm } from 'antd/es/form/Form';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';

Expand All @@ -19,29 +22,90 @@ type Deployment = NewDeployment & { createdAt?: Date };
type Props = {
deployments: Deployment[];
projectId: string;
onRefresh: () => Promise<void>;
};

const ProjectDeployments: React.FC<Props> = ({ deployments, projectId }) => {
const ProjectDeployments: React.FC<Props> = ({ deployments, projectId, onRefresh }) => {
const { t } = useTranslation();
const updateDeployment = useCreateDeployment(projectId);
const [deploymentModal, setDeploymentModal] = React.useState<boolean>(false);
const [form] = useForm();
const [currentDeployment, setCurrentDeployment] = React.useState<Deployment>();
const [addDeploymentsLoading, setAddDeploymentsLoading] = React.useState(false);

const handleSubmitUpdate = async () => {
try {
setAddDeploymentsLoading(true);
await form.validateFields();
await updateDeployment({
...currentDeployment,
...form.getFieldsValue(),
});
await onRefresh();
form.resetFields();
setDeploymentModal(false);
} finally {
setAddDeploymentsLoading(false);
}
};

return (
<>
<Modal
open={deploymentModal}
onCancel={() => setDeploymentModal(false)}
title="Edit Deployment"
width={572}
cancelButtonProps={{
style: {
display: 'none',
},
}}
okText="Update"
okButtonProps={{
shape: 'round',
size: 'large',
loading: addDeploymentsLoading,
}}
onOk={() => {
handleSubmitUpdate();
}}
>
<div style={{ padding: '12px 0' }}>
<Form form={form} layout="vertical">
<Form.Item label="Deployment Description" name="description" rules={[{ required: true }]}>
<Markdown
value={form.getFieldValue('description')}
onChange={(e) => {
form.setFieldValue('description', e);
}}
></Markdown>
</Form.Item>
</Form>
</div>
</Modal>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>{t('deployments.header1')}</TableCell>
<TableCell>RECOMMENDED</TableCell>
<TableCell>{t('deployments.header2')}</TableCell>
<TableCell>{t('deployments.header3')}</TableCell>
<TableCell>{t('deployments.header4')}</TableCell>
{/* <TableCell>{t('general.action')}</TableCell> */}
<TableCell>{t('general.action')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{deployments.map((deployment, index) => (
<TableRow>
<TableRow key={index}>
<TableCell>
<p className={styles.value}>{deployment.version}</p>
</TableCell>
<TableCell>
<p className={styles.value}>
<Radio checked>RECOMMENDED</Radio>
</p>
</TableCell>
<TableCell>
<div className={styles.deploymentId}>
<p className={styles.value}>{deployment.deploymentId}</p>
Expand All @@ -58,6 +122,18 @@ const ProjectDeployments: React.FC<Props> = ({ deployments, projectId }) => {
{deployment.createdAt ? dayjs(deployment.createdAt).utc(true).fromNow() : 'N/A'}
</p>
</TableCell>
<TableCell>
<Typography.Link
active
onClick={() => {
form.setFieldValue('description', deployment.description || '');
setCurrentDeployment(deployment);
setDeploymentModal(true);
}}
>
Edit
</Typography.Link>
</TableCell>
</TableRow>
))}
</TableBody>
Expand Down
3 changes: 2 additions & 1 deletion src/containers/ProjectRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function useProjectRegistryImpl(logger: Logger) {
id: BigNumberish,
deploymentId: string,
version: string,
recommended?: boolean,
): Promise<ContractTransaction> => {
if (!contracts) {
throw new Error('ProjectRegistry contract not available');
Expand All @@ -90,7 +91,7 @@ function useProjectRegistryImpl(logger: Logger) {
id,
cidToBytes32(deploymentId),
cidToBytes32(version),
true,
!!recommended,
);

tx.wait().then((receipt) => {
Expand Down
13 changes: 10 additions & 3 deletions src/hooks/useCreateDeployment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,26 @@ import { ContractReceipt } from '@ethersproject/contracts';
import { useProjectMetadata, useProjectRegistry } from '../containers';
import { NewDeployment } from '../models';

export function useCreateDeployment(projectId: string): (deploymentDetails: NewDeployment) => Promise<ContractReceipt> {
export function useCreateDeployment(
projectId: string,
): (deploymentDetails: NewDeployment & { recommended: boolean }) => Promise<ContractReceipt> {
const projectRegistry = useProjectRegistry();
const { uploadVersionMetadata } = useProjectMetadata();

const createDeployment = async (deploymentDetails: NewDeployment) => {
const createDeployment = async (deploymentDetails: NewDeployment & { recommended: boolean }) => {
const versionCid = await uploadVersionMetadata({
version: deploymentDetails.version,
description: deploymentDetails.description,
});

console.log('Uploaded version details', versionCid);

const tx = await projectRegistry.updateDeployment(projectId, deploymentDetails.deploymentId, versionCid);
const tx = await projectRegistry.updateDeployment(
projectId,
deploymentDetails.deploymentId,
versionCid,
deploymentDetails.recommended,
);

return await tx.wait(1);
};
Expand Down
38 changes: 31 additions & 7 deletions src/pages/studio/Project/Deployments.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { useGetProjectDeploymentsQuery } from '@subql/react-hooks';
import { forwardRef, useImperativeHandle } from 'react';
import { useGetProjectDeploymentsLazyQuery } from '@subql/react-hooks';
import { uniqBy } from 'ramda';

import { ProjectDeployments, Spinner } from '../../../components';
Expand All @@ -18,16 +19,23 @@ type Props = {
};
};

const DeploymentsTab: React.FC<Props> = ({ projectId, currentDeployment }) => {
const query = useGetProjectDeploymentsQuery({
export interface DeploymendRef {
refresh: () => void;
}

const DeploymentsTab = forwardRef<DeploymendRef, Props>(({ projectId, currentDeployment }, ref) => {
const [getProjDeployments] = useGetProjectDeploymentsLazyQuery({
variables: {
projectId,
},
});
const { catSingle } = useIPFS();

const asyncDeployments = useAsyncMemo(async () => {
let projectDeployments = query.data?.project?.deployments.nodes.filter(notEmpty);
const res = await getProjDeployments({
fetchPolicy: 'network-only',
});
let projectDeployments = res.data?.project?.deployments.nodes.filter(notEmpty);
if (!projectDeployments) {
return [];
}
Expand Down Expand Up @@ -61,10 +69,18 @@ const DeploymentsTab: React.FC<Props> = ({ projectId, currentDeployment }) => {
createdAt: deployment.createdTimestamp,
version: result.version,
description: result.description,
// TODO: backend support
recommended: true,
};
}),
);
}, [query, currentDeployment]);
}, [currentDeployment]);

useImperativeHandle(ref, () => ({
refresh: () => {
return asyncDeployments.refetch();
},
}));

return renderAsync(asyncDeployments, {
loading: () => <Spinner />,
Expand All @@ -75,9 +91,17 @@ const DeploymentsTab: React.FC<Props> = ({ projectId, currentDeployment }) => {
return <div>There has no deployments for this project</div>;
}

return <ProjectDeployments deployments={deployments} projectId={projectId} />;
return (
<ProjectDeployments
deployments={deployments}
projectId={projectId}
onRefresh={async () => {
await asyncDeployments.refetch();
}}
/>
);
},
});
};
});

export default DeploymentsTab;
32 changes: 23 additions & 9 deletions src/pages/studio/Project/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import * as React from 'react';
import { useNavigate, useParams } from 'react-router';
import { ExternalLink } from '@components/ProjectOverview/ProjectOverview';
import { Markdown, Typography } from '@subql/components';
import { Breadcrumb, Button, Form, Input, Modal } from 'antd';
import { Breadcrumb, Button, Checkbox, Form, Input, Modal } from 'antd';
import { useForm } from 'antd/es/form/Form';
import clsx from 'clsx';

import { IPFSImage, Spinner } from '../../../components';
import { useWeb3 } from '../../../containers';
import { useCreateDeployment, useProject } from '../../../hooks';
import { parseError, renderAsync } from '../../../utils';
import DeploymentsTab from './Deployments';
import DeploymentsTab, { DeploymendRef } from './Deployments';
import styles from './Project.module.css';

const Project: React.FC = () => {
Expand All @@ -24,12 +24,20 @@ const Project: React.FC = () => {
const [form] = useForm();
const [deploymentModal, setDeploymentModal] = React.useState<boolean>(false);
const createDeployment = useCreateDeployment(id ?? '');
const deploymentsRef = React.useRef<DeploymendRef>(null);
const [addDeploymentsLoading, setAddDeploymentsLoading] = React.useState(false);

const handleSubmitCreate = async () => {
await form.validateFields();
await createDeployment(form.getFieldsValue());
form.resetFields();
setDeploymentModal(false);
try {
setAddDeploymentsLoading(true);
await form.validateFields();
await createDeployment(form.getFieldsValue());
await deploymentsRef.current?.refresh();
form.resetFields();
setDeploymentModal(false);
} finally {
setAddDeploymentsLoading(false);
}
};

return renderAsync(asyncProject, {
Expand Down Expand Up @@ -63,6 +71,7 @@ const Project: React.FC = () => {
okButtonProps={{
shape: 'round',
size: 'large',
loading: addDeploymentsLoading,
}}
onOk={() => {
handleSubmitCreate();
Expand All @@ -76,11 +85,14 @@ const Project: React.FC = () => {
<Form.Item label="Version" name="version" rules={[{ required: true }]}>
<Input size="large" placeholder="Enter version"></Input>
</Form.Item>
<Form.Item label="Deployment Description" name="deploymentDesc" rules={[{ required: true }]}>
<Form.Item name="recommended">
<Checkbox>Set as recommended version</Checkbox>
</Form.Item>
<Form.Item label="Deployment Description" name="description" rules={[{ required: true }]}>
<Markdown
value={form.getFieldValue('deploymentDesc')}
value={form.getFieldValue('description')}
onChange={(e) => {
form.setFieldValue('deploymentDesc', e);
form.setFieldValue('description', e);
}}
></Markdown>
</Form.Item>
Expand Down Expand Up @@ -152,6 +164,7 @@ const Project: React.FC = () => {
{project.metadata.categories?.map((category) => {
return (
<div
key={category}
style={{
padding: '8px 16px',
background: 'rgba(67, 136, 221, 0.10)',
Expand Down Expand Up @@ -190,6 +203,7 @@ const Project: React.FC = () => {
</Typography.Link>
</div>
<DeploymentsTab
ref={deploymentsRef}
projectId={id ?? ''}
currentDeployment={project && { deployment: project.deploymentId, version: project.version }}
/>
Expand Down

0 comments on commit 60f49ea

Please sign in to comment.