diff --git a/README.md b/README.md index 4ca3a392..541fe042 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,10 @@ - Set `githubOauth.clientId` and `githubOauth.clientSecret` in backend [config file](packages/backend/environments/local.toml) - Client id and secret will be available after creating Github OAuth app - - https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app + - https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app + - In "Homepage URL", type `http://localhost:3000` + - In "Authorization callback URL", type `http://localhost:3000/projects/create` + - Generate a new client secret after app is created - Start the server @@ -40,7 +43,7 @@ yarn start ``` -- Copy the graphQL endpoint from terminal and add the endpoint in the `.env` file present in `packages/frontend` +- Copy the graphQL endpoint from terminal and add the endpoint in the [.env](packages/frontend/.env) file present in `packages/frontend` ``` REACT_APP_GQL_SERVER_URL = 'http://localhost:8000/graphql' diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index 0a21e04e..2dfef012 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -273,6 +273,15 @@ export const createResolvers = async (db: Database, app: OAuthApp): Promise log(err); return false; } + }, + + updateProdBranch: async (_: any, { projectId, prodBranch }: {projectId: string, prodBranch: string }) => { + try { + return await db.updateProjectById(projectId, { prodBranch }); + } catch (err) { + log(err); + return (false); + } } } }; diff --git a/packages/backend/src/schema.gql b/packages/backend/src/schema.gql index f2bf0db7..09c1df39 100644 --- a/packages/backend/src/schema.gql +++ b/packages/backend/src/schema.gql @@ -139,6 +139,7 @@ type Mutation { updateEnvironmentVariable(environmentVariableId: String!, environmentVariable: UpdateEnvironmentVariableInput!): Boolean! updateDeploymentToProd(deploymentId: String!): Boolean! updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean! + updateProdBranch(projectId: String!, prodBranch: String!): Boolean! redeployToProd(deploymentId: String!): Boolean! deleteProject(projectId: String!): Boolean! deleteDomain(domainId: String!): Boolean! diff --git a/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx b/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx index 34cd4c36..d6c51bf5 100644 --- a/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx +++ b/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx @@ -1,32 +1,72 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import toast from 'react-hot-toast'; +import { Project } from 'gql-client'; import { Button, Input, Switch, Typography } from '@material-tailwind/react'; -import { GitRepositoryDetails } from '../../../../types/project'; import WebhookCard from './WebhookCard'; +import { useGQLClient } from '../../../../context/GQLClientContext'; -const GitTabPanel = () => { - // TODO: Get linked repo from project - const [linkedRepo] = useState(); - const [webhooksArray, setWebhooksArray] = useState>([]); +const GitTabPanel = ({ + project, + onUpdate, +}: { + project: Project; + onUpdate: () => Promise; +}) => { + const client = useGQLClient(); const { register, handleSubmit, reset, formState: { isSubmitSuccessful }, - } = useForm(); + } = useForm({ + defaultValues: { + webhookUrl: project.webhooks, + }, + }); + + const { + register: registerProdBranch, + handleSubmit: handleSubmitProdBranch, + reset: resetProdBranch, + } = useForm({ + defaultValues: { + prodBranch: project.prodBranch, + }, + }); + + const updateProdBranchHandler = useCallback( + async (data: any) => { + const { updateProdBranch } = await client.updateProdBranch( + project.id, + data.prodBranch, + ); + + if (updateProdBranch) { + await onUpdate(); + toast.success('Production branch upadated successfully'); + } else { + toast.error('Error updating production branch'); + } + }, + [project], + ); useEffect(() => { reset(); }, [isSubmitSuccessful]); - const handleDelete = (index: number) => { - const newArray = webhooksArray.filter((value, idx) => idx != index); - setWebhooksArray(newArray); - toast.success('Webhook deleted successfully'); + useEffect(() => { + resetProdBranch({ + prodBranch: project.prodBranch, + }); + }, [project]); + + const handleDelete = () => { + // TODO: Impletement functionality to delete webhooks }; return ( @@ -55,44 +95,30 @@ const GitTabPanel = () => { -
- - Production branch - - - By default, each commit pushed to the{' '} - main branch initiates a production - deployment. You can opt for a different branch for deployment in the - settings. - - {!linkedRepo && ( -
-
^
-
- - This project isn't currently linked to a Git repository. To - establish a production branch, please linked to an existing Git - repository in the 'Connected Git Repository' section - above. - -
-
- )} - Branch name - - -
+
+
+ + Production branch + + + By default, each commit pushed to the{' '} + {project.prodBranch} branch + initiates a production deployment. You can opt for a different + branch for deployment in the settings. + + Branch name + + +
+
{ - setWebhooksArray((prevArray) => [...prevArray, data.webhookUrl]); - + onSubmit={handleSubmit(() => { toast.success('Webhook added successfully.'); })} > @@ -118,12 +144,12 @@ const GitTabPanel = () => {
- {webhooksArray?.map((webhookUrl, index) => { + {project.webhooks.map((webhookUrl, index) => { return ( handleDelete(index)} + handleDelete={() => handleDelete()} key={index} /> ); diff --git a/packages/frontend/src/pages/projects/create/index.tsx b/packages/frontend/src/pages/projects/create/index.tsx index 15608bf8..d4c99dcc 100644 --- a/packages/frontend/src/pages/projects/create/index.tsx +++ b/packages/frontend/src/pages/projects/create/index.tsx @@ -41,6 +41,7 @@ const NewProject = () => { return; } + // TODO: Handle React component error const interceptor = async (error: RequestError | Error) => { if ( error instanceof RequestError && diff --git a/packages/gql-client/src/client.ts b/packages/gql-client/src/client.ts index 55e4a6f2..6e4bbe6b 100644 --- a/packages/gql-client/src/client.ts +++ b/packages/gql-client/src/client.ts @@ -1,8 +1,8 @@ import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client'; import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject, getDomains, getProjectsInOrganization } from './queries'; -import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse, DeleteDomainResponse } from './types'; -import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain } from './mutations'; +import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse, DeleteDomainResponse, UpdateProdBranchResponse } from './types'; +import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain, updateProdBranch } from './mutations'; export interface GraphQLConfig { gqlEndpoint: string; @@ -292,4 +292,16 @@ export class GQLClient { return data; } + + async updateProdBranch (projectId: string, prodBranch: string): Promise { + const { data } = await this.client.mutate({ + mutation: updateProdBranch, + variables: { + projectId, + prodBranch + } + }); + + return data; + } } diff --git a/packages/gql-client/src/mutations.ts b/packages/gql-client/src/mutations.ts index 17949959..80342024 100644 --- a/packages/gql-client/src/mutations.ts +++ b/packages/gql-client/src/mutations.ts @@ -86,3 +86,9 @@ export const unauthenticateGitHub = gql` mutation { unauthenticateGitHub }`; + +export const updateProdBranch = gql` +mutation ($projectId: String!, $prodBranch: String!) { + updateProdBranch(projectId: $projectId, prodBranch: $prodBranch) +} +`; diff --git a/packages/gql-client/src/types.ts b/packages/gql-client/src/types.ts index 454dfd1d..debb1bc1 100644 --- a/packages/gql-client/src/types.ts +++ b/packages/gql-client/src/types.ts @@ -258,3 +258,7 @@ export type AuthenticateGitHubResponse = { export type UnauthenticateGitHubResponse = { unauthenticateGitHub: boolean } + +export type UpdateProdBranchResponse = { + updateProdBranch: boolean +}