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/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..21ff81c9 100644 --- a/packages/client/src/graphql/project/project.graphql +++ b/packages/client/src/graphql/project/project.graphql @@ -1,12 +1,23 @@ 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 + 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..045f8676 100644 --- a/packages/client/src/graphql/project/project.ts +++ b/packages/client/src/graphql/project/project.ts @@ -10,6 +10,20 @@ 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; +}>; + + +export type CreateProjectMutation = { __typename?: 'Mutation', signLabCreateProject: { __typename?: 'Project', name: string, description: string } }; + export type DeleteProjectMutationVariables = Types.Exact<{ project: Types.Scalars['ID']['input']; }>; @@ -55,6 +69,73 @@ 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) { + 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..ca515419 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 { useState } from 'react'; +import { Button, Typography } from '@mui/material'; +import { useEffect, useState } from 'react'; import { materialRenderers, materialCells } from '@jsonforms/material-renderers'; import { JsonForms } from '@jsonforms/react'; +import { useCreateProjectMutation, useProjectExistsLazyQuery } from '../../graphql/project/project'; +import { ErrorObject } from 'ajv'; const schema = { type: 'object', @@ -42,47 +44,76 @@ const uischema = { ] }; +const initialData = { + name: '', + description: '' +}; + export const NewProject: React.FC = () => { - const [error, setError] = useState(true); const navigate = useNavigate(); + 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) { + navigate('/successpage'); + } + }, [createProjectResults]); - const handleChange = (data: any) => { + useEffect(() => { + if (error) { + //handle server side error here. For now a simple text is displayed + } + }, [error]); + + 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 = (event: { preventDefault: () => void }) => { - if (error) { - setError(true); - event.preventDefault(); - return; - } else { - //submit logic - //redirect to next page - setError(false); - navigate('/successpage'); - } + + const handleSubmit = async () => { + createProject(); }; return ( <> + {error && ( + + Failed to create project! Try again. + + )} handleChange(data)} + onChange={({ data, errors }) => handleChange(data, errors)} + additionalErrors={additionalErrors} /> - 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