From e3332dac4bc6063b02e4c6b5f5336fadcfa71b0a Mon Sep 17 00:00:00 2001 From: Akosah Date: Wed, 10 Jan 2024 16:14:57 -0500 Subject: [PATCH 1/6] link create project from front end --- packages/client/src/graphql/entry.ts | 2 +- packages/client/src/graphql/graphql.ts | 2 +- .../src/graphql/project/project.graphql | 7 ++++ .../client/src/graphql/project/project.ts | 41 +++++++++++++++++++ packages/client/src/pages/SuccessPage.tsx | 2 +- .../client/src/pages/projects/NewProject.tsx | 25 +++++++++-- packages/server/.env | 2 +- 7 files changed, 73 insertions(+), 8 deletions(-) diff --git a/packages/client/src/graphql/entry.ts b/packages/client/src/graphql/entry.ts index 4585bdab..6e170a4e 100644 --- a/packages/client/src/graphql/entry.ts +++ b/packages/client/src/graphql/entry.ts @@ -10,7 +10,7 @@ export type EntryForDatasetQueryVariables = Types.Exact<{ }>; -export type EntryForDatasetQuery = { __typename?: 'Query', entryForDataset: Array<{ __typename?: 'Entry', _id: string, organization: string, entryID: string, contentType: string, dataset: string, creator?: string | null, dateCreated: any, meta: any, signedUrl: string, signedUrlExpiration: number }> }; +export type EntryForDatasetQuery = { __typename?: 'Query', entryForDataset: Array<{ __typename?: 'Entry', _id: string, organization: string, entryID: string, contentType: string, dataset: string, creator: string, dateCreated: any, meta: any, signedUrl: string, signedUrlExpiration: number }> }; export const EntryForDatasetDocument = gql` diff --git a/packages/client/src/graphql/graphql.ts b/packages/client/src/graphql/graphql.ts index ef268a85..60097ca4 100644 --- a/packages/client/src/graphql/graphql.ts +++ b/packages/client/src/graphql/graphql.ts @@ -69,7 +69,7 @@ export type Entry = { __typename?: 'Entry'; _id: Scalars['String']['output']; contentType: Scalars['String']['output']; - creator?: Maybe; + creator: Scalars['ID']['output']; dataset: Scalars['ID']['output']; dateCreated: Scalars['DateTime']['output']; entryID: Scalars['String']['output']; diff --git a/packages/client/src/graphql/project/project.graphql b/packages/client/src/graphql/project/project.graphql index 4e38a75b..4023bce0 100644 --- a/packages/client/src/graphql/project/project.graphql +++ b/packages/client/src/graphql/project/project.graphql @@ -7,6 +7,13 @@ query getProjects { } } +mutation createProject($project: ProjectCreate!) { + signLabCreateProject(project: $project) { + name, + description + } +} + mutation deleteProject($project: ID!) { deleteProject(project: $project) } diff --git a/packages/client/src/graphql/project/project.ts b/packages/client/src/graphql/project/project.ts index 683052c5..07f69b5c 100644 --- a/packages/client/src/graphql/project/project.ts +++ b/packages/client/src/graphql/project/project.ts @@ -10,6 +10,13 @@ export type GetProjectsQueryVariables = Types.Exact<{ [key: string]: never; }>; export type GetProjectsQuery = { __typename?: 'Query', getProjects: Array<{ __typename?: 'Project', _id: string, name: string, description: string, created: any }> }; +export type CreateProjectMutationVariables = Types.Exact<{ + project: Types.ProjectCreate; +}>; + + +export type CreateProjectMutation = { __typename?: 'Mutation', signLabCreateProject: { __typename?: 'Project', name: string, description: string } }; + export type DeleteProjectMutationVariables = Types.Exact<{ project: Types.Scalars['ID']['input']; }>; @@ -55,6 +62,40 @@ export function useGetProjectsLazyQuery(baseOptions?: Apollo.LazyQueryHookOption export type GetProjectsQueryHookResult = ReturnType; export type GetProjectsLazyQueryHookResult = ReturnType; export type GetProjectsQueryResult = Apollo.QueryResult; +export const CreateProjectDocument = gql` + mutation createProject($project: ProjectCreate!) { + signLabCreateProject(project: $project) { + name + description + } +} + `; +export type CreateProjectMutationFn = Apollo.MutationFunction; + +/** + * __useCreateProjectMutation__ + * + * To run a mutation, you first call `useCreateProjectMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateProjectMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [createProjectMutation, { data, loading, error }] = useCreateProjectMutation({ + * variables: { + * project: // value for 'project' + * }, + * }); + */ +export function useCreateProjectMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(CreateProjectDocument, options); + } +export type CreateProjectMutationHookResult = ReturnType; +export type CreateProjectMutationResult = Apollo.MutationResult; +export type CreateProjectMutationOptions = Apollo.BaseMutationOptions; export const DeleteProjectDocument = gql` mutation deleteProject($project: ID!) { deleteProject(project: $project) diff --git a/packages/client/src/pages/SuccessPage.tsx b/packages/client/src/pages/SuccessPage.tsx index bbc322cb..3ac27e80 100644 --- a/packages/client/src/pages/SuccessPage.tsx +++ b/packages/client/src/pages/SuccessPage.tsx @@ -4,7 +4,7 @@ export const SuccessPage = () => { return (
Successfully created! -
diff --git a/packages/client/src/pages/projects/NewProject.tsx b/packages/client/src/pages/projects/NewProject.tsx index ad7e415d..e5e7411e 100644 --- a/packages/client/src/pages/projects/NewProject.tsx +++ b/packages/client/src/pages/projects/NewProject.tsx @@ -1,8 +1,10 @@ import { useNavigate } from 'react-router-dom'; -import { Button } from '@mui/material'; +import { Button, Typography } from '@mui/material'; import { useState } from 'react'; import { materialRenderers, materialCells } from '@jsonforms/material-renderers'; import { JsonForms } from '@jsonforms/react'; +import { useApolloClient } from '@apollo/client'; +import { CreateProjectDocument } from '../../graphql/project/project'; const schema = { type: 'object', @@ -45,6 +47,7 @@ const uischema = { export const NewProject: React.FC = () => { const [error, setError] = useState(true); const navigate = useNavigate(); + const apolloClient = useApolloClient(); const initialData = { name: '', @@ -59,13 +62,22 @@ export const NewProject: React.FC = () => { setError(false); } }; - const handleSubmit = (event: { preventDefault: () => void }) => { + const handleSubmit = async (event: { preventDefault: () => void }) => { if (error) { setError(true); event.preventDefault(); return; } else { - //submit logic + const result = await apolloClient.mutate({ + mutation: CreateProjectDocument, + variables: { project: data } + }); + + if (result.errors || !result.data) { + console.error('Failed to create study'); + setError(true); + return; + } //redirect to next page setError(false); navigate('/successpage'); @@ -74,6 +86,11 @@ export const NewProject: React.FC = () => { return ( <> + {error && ( + + Failed to create project! Try again. + + )} { cells={materialCells} onChange={({ data }) => handleChange(data)} /> - diff --git a/packages/server/.env b/packages/server/.env index d589b4f7..8f647f60 100644 --- a/packages/server/.env +++ b/packages/server/.env @@ -1 +1 @@ -GCP_KEY_FILENAME=../../../../../../certificates/signlab-dev-key.json +GCP_KEY_FILENAME=../../../certificates/signlab-dev-key.json From b4d50a1ccbf2800e3a74ae3cd32448f5513e9e1b Mon Sep 17 00:00:00 2001 From: Akosah Date: Wed, 10 Jan 2024 16:29:18 -0500 Subject: [PATCH 2/6] remove unintended changes --- packages/server/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/.env b/packages/server/.env index 8f647f60..d589b4f7 100644 --- a/packages/server/.env +++ b/packages/server/.env @@ -1 +1 @@ -GCP_KEY_FILENAME=../../../certificates/signlab-dev-key.json +GCP_KEY_FILENAME=../../../../../../certificates/signlab-dev-key.json From 4d92d293115dc0f4bf4dce204496e1b09b2e8ed2 Mon Sep 17 00:00:00 2001 From: Akosah Date: Wed, 10 Jan 2024 16:37:17 -0500 Subject: [PATCH 3/6] ignore server env file --- .gitignore | 1 + packages/server/.env | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 packages/server/.env diff --git a/.gitignore b/.gitignore index d93463d4..85eaf808 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ build/ +**/.env \ No newline at end of file diff --git a/packages/server/.env b/packages/server/.env deleted file mode 100644 index d589b4f7..00000000 --- a/packages/server/.env +++ /dev/null @@ -1 +0,0 @@ -GCP_KEY_FILENAME=../../../../../../certificates/signlab-dev-key.json From 2f05c0f9aaab47dc1ae0de5c367ecaa6ef65ede9 Mon Sep 17 00:00:00 2001 From: Akosah Date: Thu, 11 Jan 2024 10:35:05 -0500 Subject: [PATCH 4/6] use apollo hooks --- .../src/graphql/project/project.graphql | 12 ++- .../client/src/graphql/project/project.ts | 40 +++++++++ .../client/src/pages/projects/NewProject.tsx | 83 +++++++++++-------- 3 files changed, 97 insertions(+), 38 deletions(-) diff --git a/packages/client/src/graphql/project/project.graphql b/packages/client/src/graphql/project/project.graphql index 4023bce0..21ff81c9 100644 --- a/packages/client/src/graphql/project/project.graphql +++ b/packages/client/src/graphql/project/project.graphql @@ -1,15 +1,19 @@ query getProjects { getProjects { - _id, - name, - description, + _id + name + description created } } +query projectExists($name: String!) { + projectExists(name: $name) +} + mutation createProject($project: ProjectCreate!) { signLabCreateProject(project: $project) { - name, + name description } } diff --git a/packages/client/src/graphql/project/project.ts b/packages/client/src/graphql/project/project.ts index 07f69b5c..045f8676 100644 --- a/packages/client/src/graphql/project/project.ts +++ b/packages/client/src/graphql/project/project.ts @@ -10,6 +10,13 @@ export type GetProjectsQueryVariables = Types.Exact<{ [key: string]: never; }>; export type GetProjectsQuery = { __typename?: 'Query', getProjects: Array<{ __typename?: 'Project', _id: string, name: string, description: string, created: any }> }; +export type ProjectExistsQueryVariables = Types.Exact<{ + name: Types.Scalars['String']['input']; +}>; + + +export type ProjectExistsQuery = { __typename?: 'Query', projectExists: boolean }; + export type CreateProjectMutationVariables = Types.Exact<{ project: Types.ProjectCreate; }>; @@ -62,6 +69,39 @@ export function useGetProjectsLazyQuery(baseOptions?: Apollo.LazyQueryHookOption export type GetProjectsQueryHookResult = ReturnType; export type GetProjectsLazyQueryHookResult = ReturnType; export type GetProjectsQueryResult = Apollo.QueryResult; +export const ProjectExistsDocument = gql` + query projectExists($name: String!) { + projectExists(name: $name) +} + `; + +/** + * __useProjectExistsQuery__ + * + * To run a query within a React component, call `useProjectExistsQuery` and pass it any options that fit your needs. + * When your component renders, `useProjectExistsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useProjectExistsQuery({ + * variables: { + * name: // value for 'name' + * }, + * }); + */ +export function useProjectExistsQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(ProjectExistsDocument, options); + } +export function useProjectExistsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(ProjectExistsDocument, options); + } +export type ProjectExistsQueryHookResult = ReturnType; +export type ProjectExistsLazyQueryHookResult = ReturnType; +export type ProjectExistsQueryResult = Apollo.QueryResult; export const CreateProjectDocument = gql` mutation createProject($project: ProjectCreate!) { signLabCreateProject(project: $project) { diff --git a/packages/client/src/pages/projects/NewProject.tsx b/packages/client/src/pages/projects/NewProject.tsx index e5e7411e..8b1d03f5 100644 --- a/packages/client/src/pages/projects/NewProject.tsx +++ b/packages/client/src/pages/projects/NewProject.tsx @@ -1,10 +1,10 @@ import { useNavigate } from 'react-router-dom'; import { Button, Typography } from '@mui/material'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { materialRenderers, materialCells } from '@jsonforms/material-renderers'; import { JsonForms } from '@jsonforms/react'; -import { useApolloClient } from '@apollo/client'; -import { CreateProjectDocument } from '../../graphql/project/project'; +import { useCreateProjectMutation, useProjectExistsLazyQuery } from '../../graphql/project/project'; +import { ErrorObject } from 'ajv'; const schema = { type: 'object', @@ -44,44 +44,58 @@ const uischema = { ] }; +const initialData = { + name: '', + description: '' +}; + export const NewProject: React.FC = () => { - const [error, setError] = useState(true); const navigate = useNavigate(); - const apolloClient = useApolloClient(); + const [data, setData] = useState(initialData); + const [createProject, { error, data: createProjectResults, loading }] = useCreateProjectMutation({ + variables: { project: data } + }); + const [projectExistsQuery, projectExistsResults] = useProjectExistsLazyQuery(); + const [additionalErrors, setAdditionalErrors] = useState([]); - const initialData = { - name: '', - description: '' - }; + useEffect(() => { + if (projectExistsResults.data?.projectExists) { + setAdditionalErrors([ + { + instancePath: '/name', + keyword: 'uniqueProjectName', + message: 'A project with this name already exists', + schemaPath: '#/properties/name/name', + params: { keyword: 'uniqueProjectName' } + } + ]); + } else { + setAdditionalErrors([]); + } + }, [projectExistsResults.data]); - const [data, setData] = useState(initialData); + useEffect(() => { + if (createProjectResults) { + console.log('succesfully created'); + navigate('/successpage'); + } + }, [createProjectResults]); + + useEffect(() => { + if (error) { + //handle server side error here. For now a simple text is displayed + } + }, [error]); - const handleChange = (data: any) => { + const handleChange = (data: any, errors: ErrorObject[] | undefined) => { setData(data); - if (data) { - setError(false); + if (!errors || errors.length === 0) { + projectExistsQuery({ variables: { name: data.name } }); } }; - const handleSubmit = async (event: { preventDefault: () => void }) => { - if (error) { - setError(true); - event.preventDefault(); - return; - } else { - const result = await apolloClient.mutate({ - mutation: CreateProjectDocument, - variables: { project: data } - }); - if (result.errors || !result.data) { - console.error('Failed to create study'); - setError(true); - return; - } - //redirect to next page - setError(false); - navigate('/successpage'); - } + const handleSubmit = async () => { + createProject(); }; return ( @@ -97,9 +111,10 @@ export const NewProject: React.FC = () => { data={data} renderers={materialRenderers} cells={materialCells} - onChange={({ data }) => handleChange(data)} + onChange={({ data, errors }) => handleChange(data, errors)} + additionalErrors={additionalErrors} /> - From 67cc07b4a4ab8a9ae556f5b66242476cc55fe578 Mon Sep 17 00:00:00 2001 From: Akosah Date: Thu, 11 Jan 2024 11:27:03 -0500 Subject: [PATCH 5/6] disable button on form error --- packages/client/src/pages/projects/NewProject.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/pages/projects/NewProject.tsx b/packages/client/src/pages/projects/NewProject.tsx index 8b1d03f5..e42d0a61 100644 --- a/packages/client/src/pages/projects/NewProject.tsx +++ b/packages/client/src/pages/projects/NewProject.tsx @@ -114,7 +114,7 @@ export const NewProject: React.FC = () => { onChange={({ data, errors }) => handleChange(data, errors)} additionalErrors={additionalErrors} /> - From 7c0abfce839e492ef6703104b33dfd55023539e8 Mon Sep 17 00:00:00 2001 From: Akosah Date: Thu, 11 Jan 2024 12:24:36 -0500 Subject: [PATCH 6/6] unneeded log removed --- packages/client/src/pages/projects/NewProject.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/pages/projects/NewProject.tsx b/packages/client/src/pages/projects/NewProject.tsx index e42d0a61..ca515419 100644 --- a/packages/client/src/pages/projects/NewProject.tsx +++ b/packages/client/src/pages/projects/NewProject.tsx @@ -76,7 +76,6 @@ export const NewProject: React.FC = () => { useEffect(() => { if (createProjectResults) { - console.log('succesfully created'); navigate('/successpage'); } }, [createProjectResults]);