diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 0eb8897a..6eb692f0 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -18,15 +18,18 @@ import { LoginPage } from './pages/LoginPage'; import { DatasetControls } from './pages/datasets/DatasetControls'; import { AuthCallback } from './pages/AuthCallback'; import { EnvironmentContextProvider } from './context/EnvironmentContext'; -import { AuthProvider } from './context/AuthContext'; +import { AuthProvider, useAuth, AUTH_TOKEN_STR } from './context/AuthContext'; import { AdminGuard } from './guards/AdminGuard'; import { LogoutPage } from './pages/LogoutPage'; import { CssBaseline, Box, styled } from '@mui/material'; -import { useState } from 'react'; +import { FC, ReactNode, useState } from 'react'; import { SideBar } from './components/SideBar'; +import { ProjectProvider } from './context/ProjectContext'; +import { ApolloClient, ApolloProvider, InMemoryCache, concat, createHttpLink } from '@apollo/client'; +import { setContext } from '@apollo/client/link/context'; +import {StudyProvider} from './context/Study'; const drawerWidth = 256; - const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })<{ open?: boolean; }>(({ theme, open }) => ({ @@ -46,50 +49,91 @@ const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })<{ }) })); -function App() { - const [drawerOpen, setDrawerOpen] = useState(true); +const App: FC = () => { + const httpLink = createHttpLink({ uri: import.meta.env.VITE_GRAPHQL_ENDPOINT }); + const authLink = setContext((_, { headers }) => { + const token = localStorage.getItem(AUTH_TOKEN_STR); + return { + headers: { + ...headers, + authorization: token ? `Bearer ${token}` : '', + } + } + }); + + const apolloClient = new ApolloClient({ + cache: new InMemoryCache(), + link: concat(authLink, httpLink) + }); return ( - - - - - - - - - - - } /> - } /> - } /> - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - - - + + + + + + ); } +const AppInternal: FC = () => { + const [drawerOpen, setDrawerOpen] = useState(true); + const { authenticated } = useAuth(); + + const mainView: ReactNode = ( + + + + + + + + + + + + + + + + ); + + return (<>{ authenticated ? mainView : }>); +}; + +const UnauthenticatedView: FC = () => { + return ; +}; + +const MyRoutes: FC = () => { + return ( + + } /> + } /> + } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + ); +}; + export default App; diff --git a/packages/client/src/components/Environment.tsx b/packages/client/src/components/Environment.tsx index c24a6f23..652c61a2 100644 --- a/packages/client/src/components/Environment.tsx +++ b/packages/client/src/components/Environment.tsx @@ -1,57 +1,63 @@ -import { Box, Accordion, Button, Link } from '@mui/material'; -import AccordionSummary from '@mui/material/AccordionSummary'; -import AccordionDetails from '@mui/material/AccordionDetails'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import { useContext } from 'react'; -import { EnvironmentContext } from '../context/EnvironmentContext'; +import { Select, MenuItem, FormControl, InputLabel, Stack, Paper, Typography } from '@mui/material'; import { useProject } from '../context/ProjectContext'; -import { ProjectModel } from '../graphql/graphql'; +import { useStudy } from '../context/Study'; +import { Dispatch, SetStateAction, FC } from 'react'; -export const Environment: React.FC = () => { - const { study } = useContext(EnvironmentContext); - const { project, updateProject } = useProject(); +export const Environment: FC = () => { + const { project, projects, setProject } = useProject(); + const { study, studies, setStudy } = useStudy(); - const handleClick = (newValue: string) => { - const newProject: ProjectModel = { name: newValue } as any; - updateProject(newProject); - }; + return ( + + Environment + + {/* Project Selection */} + option._id} + display={(option) => option.name} + /> + {/* Study Selection */} + option._id} + display={(option) => option.name} + /> + + + ); +}; + +interface FieldSelectorProps { + value: T | null, + setValue: Dispatch>; + label: string; + options: T[]; + getKey: (option: T) => string; + display: (option: T) => string; +} - const items = [ - { - name: `Project: ${project?.name}`, - subitems: [{ title: 'Project name 1' }, { title: 'Project name 2' }] - }, - { - name: `Study: ${study}`, - subitems: [{ title: 'Study name 1' }, { title: 'Study name 2' }] +function FieldSelector(props: FieldSelectorProps) { + const handleChange = (newValue: string | T) => { + if (typeof newValue == 'string') { + props.setValue(null); + return; } - ]; + props.setValue(newValue); + }; return ( - - {items?.map((item: any) => ( - - }> - {item.name} - - - {item.subitems?.map((subitem: any) => ( - - handleClick(subitem)} - > - {subitem.title} - - - ))} - - - ))} - + + {props.label} + handleChange(event.target.value)} renderValue={(option) => props.display(option)}> + {props.options.map((option) => {props.display(option)})} + + ); }; diff --git a/packages/client/src/components/SideBar.tsx b/packages/client/src/components/SideBar.tsx index 374f8b1d..0af456b5 100644 --- a/packages/client/src/components/SideBar.tsx +++ b/packages/client/src/components/SideBar.tsx @@ -2,7 +2,8 @@ import { FC, ReactNode, useState } from 'react'; import { Collapse, Divider, Drawer, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; import { ExpandMore, ExpandLess, School, Dataset, Work, Logout, GroupWork } from '@mui/icons-material'; import { useAuth } from '../context/AuthContext'; -import {useNavigate} from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; +import { Environment } from './Environment'; interface SideBarProps { open: boolean; @@ -71,16 +72,17 @@ export const SideBar: FC = ({ open, drawerWidth }) => { boxSizing: 'border-box', backgroundColor: '#103F68', color: 'white', - paddingTop: 18, + paddingTop: 2, mt: '64px' } }} anchor='left' open={open} > - + {navItems.map((navItem) => )} + ); }; diff --git a/packages/client/src/context/AuthContext.tsx b/packages/client/src/context/AuthContext.tsx index 508dae5a..cf1579d6 100644 --- a/packages/client/src/context/AuthContext.tsx +++ b/packages/client/src/context/AuthContext.tsx @@ -2,7 +2,7 @@ import { createContext, FC, useContext, useEffect, useState, ReactNode } from 'r import jwt_decode from 'jwt-decode'; import { useNavigate } from 'react-router-dom'; -const AUTH_TOKEN_STR = 'token'; +export const AUTH_TOKEN_STR = 'token'; export interface DecodedToken { id: string; @@ -63,7 +63,7 @@ export const AuthProvider: FC = (props) => { // If not token present, redirect to login if (!token) { setUnautheticated(); - navigate('/login'); + navigate('/loginpage'); return; } @@ -74,7 +74,7 @@ export const AuthProvider: FC = (props) => { // Handle expired token if (currentTime > decodedToken.exp) { setUnautheticated(); - navigate('/login'); + navigate('/loginpage'); return; } diff --git a/packages/client/src/context/ProjectContext.tsx b/packages/client/src/context/ProjectContext.tsx index e413b7df..fe426bd1 100644 --- a/packages/client/src/context/ProjectContext.tsx +++ b/packages/client/src/context/ProjectContext.tsx @@ -1,12 +1,12 @@ -import React, { createContext, FC, useContext, useEffect, useState } from 'react'; -import { ProjectModel } from '../graphql/graphql'; -import { useGetProjectLazyQuery } from '../graphql/project/project'; -import { createTheme, ThemeProvider, useTheme } from '@mui/material'; -import { useAuth } from '../context/AuthContext'; +import { createContext, Dispatch, FC, SetStateAction, useContext, useEffect, useState } from 'react'; +import { Project } from '../graphql/graphql'; +import { useGetProjectsQuery } from '../graphql/project/project'; export interface ProjectContextProps { - project?: ProjectModel; - updateProject: (updatedProject: ProjectModel) => void; + project: Project | null; + setProject: Dispatch>; + projects: Project[]; + updateProjectList: () => void; } const ProjectContext = createContext({} as ProjectContextProps); @@ -15,42 +15,27 @@ export interface ProjectProviderProps { children: React.ReactNode; } -export const ProjectProvider: FC = (props) => { - const [project, setProject] = useState(); - const { decoded_token } = useAuth(); - const [getProject] = useGetProjectLazyQuery(); - const theme = useTheme(); - const [projectTheme, setProjectTheme] = useState(theme); +export const ProjectProvider: FC = ({ children }) => { + const [project, setProject] = useState(null); + const [projects, setProjects] = useState([]); + // Query for projects + const getProjectResults = useGetProjectsQuery(); useEffect(() => { - if (decoded_token?.projectId) { - getProject({ variables: { id: decoded_token.projectId } }).then((data: any) => { - if (data?.getProject) { - setProject(data.getProject as ProjectModel); - setProjectTheme( - createTheme({ - ...theme, - ...data.getProject.muiTheme - }) - ); - } - }); + if (getProjectResults.data) { + setProjects(getProjectResults.data.getProjects); } - }, [decoded_token]); - const updateProject = (updatedProject: ProjectModel) => { - setProject(updatedProject); - }; + }, [getProjectResults.data, getProjectResults.error]); return ( - - - {props.children} - + getProjectResults.refetch() + }}> + {children} ); }; diff --git a/packages/client/src/context/Study.tsx b/packages/client/src/context/Study.tsx new file mode 100644 index 00000000..41bee296 --- /dev/null +++ b/packages/client/src/context/Study.tsx @@ -0,0 +1,45 @@ +import { Dispatch, FC, ReactNode, SetStateAction, createContext, useContext, useState, useEffect } from 'react'; +import { Study } from '../graphql/graphql'; +import { useProject } from './ProjectContext'; +import {useFindStudiesLazyQuery} from '../graphql/study/study'; + +export interface StudyContextProps { + study: Study | null; + setStudy: Dispatch>; + studies: Study[]; +} + +const StudyContext = createContext({} as StudyContextProps); + +export interface StudyProviderProps { + children: ReactNode; +} + +export const StudyProvider: FC = (props) => { + const [study, setStudy] = useState(null); + const [studies, setStudies] = useState([]); + + const [findStudies, findStudiesResults] = useFindStudiesLazyQuery(); + + const { project } = useProject(); + + // Effect to re-query for studies + useEffect(() => { + if (!project) { + setStudies([]); + return; + } + findStudies({ variables: { project: project._id } }); + }, [project]); + + // Effect to update list of studies + useEffect(() => { + if (findStudiesResults.data) { + setStudies(findStudiesResults.data.findStudies); + } + }, [findStudiesResults]); + + return {props.children}; +}; + +export const useStudy = () => useContext(StudyContext); diff --git a/packages/client/src/graphql/graphql.ts b/packages/client/src/graphql/graphql.ts index a2c17d5d..38b86997 100644 --- a/packages/client/src/graphql/graphql.ts +++ b/packages/client/src/graphql/graphql.ts @@ -489,6 +489,11 @@ export type QueryExistsArgs = { }; +export type QueryFindStudiesArgs = { + project: Scalars['ID']['input']; +}; + + export type QueryGetProjectArgs = { id: Scalars['String']['input']; }; diff --git a/packages/client/src/graphql/project/project.graphql b/packages/client/src/graphql/project/project.graphql index 5dd1fb64..d5920dd4 100644 --- a/packages/client/src/graphql/project/project.graphql +++ b/packages/client/src/graphql/project/project.graphql @@ -1,92 +1,8 @@ -query getProject($id: String!) { - getProject(id: $id) { - id - name - description - logo - muiTheme - homePage - redirectUrl - createdAt - updatedAt - deletedAt - settings { - allowSignup - displayProjectName - } - authMethods { - emailAuth - googleAuth - } - } -} - -query listProjects { - listProjects { - id - name - description - logo - } -} - -mutation updateProjectSettings($id: String!, $displayProjectName: Boolean, $allowSignup: Boolean) { - updateProjectSettings(id: $id, projectSettings: { displayProjectName: $displayProjectName, allowSignup: $allowSignup }) { - id - name - description - logo - homePage - redirectUrl - settings { - displayProjectName - allowSignup - } - authMethods { - googleAuth - emailAuth - } - } -} - -mutation updateProjectAuthMethods($id: String!, $googleAuth: Boolean, $emailAuth: Boolean) { - updateProjectAuthMethods(id: $id, projectAuthMethods: { googleAuth: $googleAuth, emailAuth: $emailAuth }) { - id - name - description - logo - homePage - redirectUrl - settings { - displayProjectName - allowSignup - } - authMethods { - googleAuth - emailAuth - } - } -} - -mutation updateProject($id: String!, $name: String, $description: String, $logo: String, $muiTheme: JSON, $homePage: String, $redirectUrl: String) { - updateProject(id: $id, settings: { name: $name, description: $description, logo: $logo, muiTheme: $muiTheme, homePage: $homePage, redirectUrl: $redirectUrl }) { - id - name - description - logo - muiTheme - homePage - redirectUrl - createdAt - updatedAt - deletedAt - settings { - displayProjectName - allowSignup - } - authMethods { - googleAuth - emailAuth - } +query getProjects { + getProjects { + _id, + name, + description, + created } } diff --git a/packages/client/src/graphql/project/project.ts b/packages/client/src/graphql/project/project.ts index 39b30e0b..a2462004 100644 --- a/packages/client/src/graphql/project/project.ts +++ b/packages/client/src/graphql/project/project.ts @@ -5,297 +5,46 @@ import * as Types from '../graphql'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; -export type GetProjectQueryVariables = Types.Exact<{ - id: Types.Scalars['String']['input']; -}>; +export type GetProjectsQueryVariables = Types.Exact<{ [key: string]: never; }>; -export type GetProjectQuery = { __typename?: 'Query', getProject: { __typename?: 'ProjectModel', id: string, name: string, description?: string | null, logo?: string | null, muiTheme: any, homePage?: string | null, redirectUrl?: string | null, createdAt: any, updatedAt: any, deletedAt?: any | null, settings: { __typename?: 'ProjectSettingsModel', allowSignup: boolean, displayProjectName: boolean }, authMethods: { __typename?: 'ProjectAuthMethodsModel', emailAuth: boolean, googleAuth: boolean } } }; +export type GetProjectsQuery = { __typename?: 'Query', getProjects: Array<{ __typename?: 'Project', _id: string, name: string, description: string, created: any }> }; -export type ListProjectsQueryVariables = Types.Exact<{ [key: string]: never; }>; - -export type ListProjectsQuery = { __typename?: 'Query', listProjects: Array<{ __typename?: 'ProjectModel', id: string, name: string, description?: string | null, logo?: string | null }> }; - -export type UpdateProjectSettingsMutationVariables = Types.Exact<{ - id: Types.Scalars['String']['input']; - displayProjectName?: Types.InputMaybe; - allowSignup?: Types.InputMaybe; -}>; - - -export type UpdateProjectSettingsMutation = { __typename?: 'Mutation', updateProjectSettings: { __typename?: 'ProjectModel', id: string, name: string, description?: string | null, logo?: string | null, homePage?: string | null, redirectUrl?: string | null, settings: { __typename?: 'ProjectSettingsModel', displayProjectName: boolean, allowSignup: boolean }, authMethods: { __typename?: 'ProjectAuthMethodsModel', googleAuth: boolean, emailAuth: boolean } } }; - -export type UpdateProjectAuthMethodsMutationVariables = Types.Exact<{ - id: Types.Scalars['String']['input']; - googleAuth?: Types.InputMaybe; - emailAuth?: Types.InputMaybe; -}>; - - -export type UpdateProjectAuthMethodsMutation = { __typename?: 'Mutation', updateProjectAuthMethods: { __typename?: 'ProjectModel', id: string, name: string, description?: string | null, logo?: string | null, homePage?: string | null, redirectUrl?: string | null, settings: { __typename?: 'ProjectSettingsModel', displayProjectName: boolean, allowSignup: boolean }, authMethods: { __typename?: 'ProjectAuthMethodsModel', googleAuth: boolean, emailAuth: boolean } } }; - -export type UpdateProjectMutationVariables = Types.Exact<{ - id: Types.Scalars['String']['input']; - name?: Types.InputMaybe; - description?: Types.InputMaybe; - logo?: Types.InputMaybe; - muiTheme?: Types.InputMaybe; - homePage?: Types.InputMaybe; - redirectUrl?: Types.InputMaybe; -}>; - - -export type UpdateProjectMutation = { __typename?: 'Mutation', updateProject: { __typename?: 'ProjectModel', id: string, name: string, description?: string | null, logo?: string | null, muiTheme: any, homePage?: string | null, redirectUrl?: string | null, createdAt: any, updatedAt: any, deletedAt?: any | null, settings: { __typename?: 'ProjectSettingsModel', displayProjectName: boolean, allowSignup: boolean }, authMethods: { __typename?: 'ProjectAuthMethodsModel', googleAuth: boolean, emailAuth: boolean } } }; - - -export const GetProjectDocument = gql` - query getProject($id: String!) { - getProject(id: $id) { - id - name - description - logo - muiTheme - homePage - redirectUrl - createdAt - updatedAt - deletedAt - settings { - allowSignup - displayProjectName - } - authMethods { - emailAuth - googleAuth - } - } -} - `; - -/** - * __useGetProjectQuery__ - * - * To run a query within a React component, call `useGetProjectQuery` and pass it any options that fit your needs. - * When your component renders, `useGetProjectQuery` 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 } = useGetProjectQuery({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useGetProjectQuery(baseOptions: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(GetProjectDocument, options); - } -export function useGetProjectLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(GetProjectDocument, options); - } -export type GetProjectQueryHookResult = ReturnType; -export type GetProjectLazyQueryHookResult = ReturnType; -export type GetProjectQueryResult = Apollo.QueryResult; -export const ListProjectsDocument = gql` - query listProjects { - listProjects { - id +export const GetProjectsDocument = gql` + query getProjects { + getProjects { + _id name description - logo + created } } `; /** - * __useListProjectsQuery__ + * __useGetProjectsQuery__ * - * To run a query within a React component, call `useListProjectsQuery` and pass it any options that fit your needs. - * When your component renders, `useListProjectsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useGetProjectsQuery` and pass it any options that fit your needs. + * When your component renders, `useGetProjectsQuery` 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 } = useListProjectsQuery({ + * const { data, loading, error } = useGetProjectsQuery({ * variables: { * }, * }); */ -export function useListProjectsQuery(baseOptions?: Apollo.QueryHookOptions) { +export function useGetProjectsQuery(baseOptions?: Apollo.QueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(ListProjectsDocument, options); + return Apollo.useQuery(GetProjectsDocument, options); } -export function useListProjectsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { +export function useGetProjectsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(ListProjectsDocument, options); + return Apollo.useLazyQuery(GetProjectsDocument, options); } -export type ListProjectsQueryHookResult = ReturnType; -export type ListProjectsLazyQueryHookResult = ReturnType; -export type ListProjectsQueryResult = Apollo.QueryResult; -export const UpdateProjectSettingsDocument = gql` - mutation updateProjectSettings($id: String!, $displayProjectName: Boolean, $allowSignup: Boolean) { - updateProjectSettings( - id: $id - projectSettings: {displayProjectName: $displayProjectName, allowSignup: $allowSignup} - ) { - id - name - description - logo - homePage - redirectUrl - settings { - displayProjectName - allowSignup - } - authMethods { - googleAuth - emailAuth - } - } -} - `; -export type UpdateProjectSettingsMutationFn = Apollo.MutationFunction; - -/** - * __useUpdateProjectSettingsMutation__ - * - * To run a mutation, you first call `useUpdateProjectSettingsMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateProjectSettingsMutation` 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 [updateProjectSettingsMutation, { data, loading, error }] = useUpdateProjectSettingsMutation({ - * variables: { - * id: // value for 'id' - * displayProjectName: // value for 'displayProjectName' - * allowSignup: // value for 'allowSignup' - * }, - * }); - */ -export function useUpdateProjectSettingsMutation(baseOptions?: Apollo.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useMutation(UpdateProjectSettingsDocument, options); - } -export type UpdateProjectSettingsMutationHookResult = ReturnType; -export type UpdateProjectSettingsMutationResult = Apollo.MutationResult; -export type UpdateProjectSettingsMutationOptions = Apollo.BaseMutationOptions; -export const UpdateProjectAuthMethodsDocument = gql` - mutation updateProjectAuthMethods($id: String!, $googleAuth: Boolean, $emailAuth: Boolean) { - updateProjectAuthMethods( - id: $id - projectAuthMethods: {googleAuth: $googleAuth, emailAuth: $emailAuth} - ) { - id - name - description - logo - homePage - redirectUrl - settings { - displayProjectName - allowSignup - } - authMethods { - googleAuth - emailAuth - } - } -} - `; -export type UpdateProjectAuthMethodsMutationFn = Apollo.MutationFunction; - -/** - * __useUpdateProjectAuthMethodsMutation__ - * - * To run a mutation, you first call `useUpdateProjectAuthMethodsMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateProjectAuthMethodsMutation` 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 [updateProjectAuthMethodsMutation, { data, loading, error }] = useUpdateProjectAuthMethodsMutation({ - * variables: { - * id: // value for 'id' - * googleAuth: // value for 'googleAuth' - * emailAuth: // value for 'emailAuth' - * }, - * }); - */ -export function useUpdateProjectAuthMethodsMutation(baseOptions?: Apollo.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useMutation(UpdateProjectAuthMethodsDocument, options); - } -export type UpdateProjectAuthMethodsMutationHookResult = ReturnType; -export type UpdateProjectAuthMethodsMutationResult = Apollo.MutationResult; -export type UpdateProjectAuthMethodsMutationOptions = Apollo.BaseMutationOptions; -export const UpdateProjectDocument = gql` - mutation updateProject($id: String!, $name: String, $description: String, $logo: String, $muiTheme: JSON, $homePage: String, $redirectUrl: String) { - updateProject( - id: $id - settings: {name: $name, description: $description, logo: $logo, muiTheme: $muiTheme, homePage: $homePage, redirectUrl: $redirectUrl} - ) { - id - name - description - logo - muiTheme - homePage - redirectUrl - createdAt - updatedAt - deletedAt - settings { - displayProjectName - allowSignup - } - authMethods { - googleAuth - emailAuth - } - } -} - `; -export type UpdateProjectMutationFn = Apollo.MutationFunction; - -/** - * __useUpdateProjectMutation__ - * - * To run a mutation, you first call `useUpdateProjectMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateProjectMutation` 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 [updateProjectMutation, { data, loading, error }] = useUpdateProjectMutation({ - * variables: { - * id: // value for 'id' - * name: // value for 'name' - * description: // value for 'description' - * logo: // value for 'logo' - * muiTheme: // value for 'muiTheme' - * homePage: // value for 'homePage' - * redirectUrl: // value for 'redirectUrl' - * }, - * }); - */ -export function useUpdateProjectMutation(baseOptions?: Apollo.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useMutation(UpdateProjectDocument, options); - } -export type UpdateProjectMutationHookResult = ReturnType; -export type UpdateProjectMutationResult = Apollo.MutationResult; -export type UpdateProjectMutationOptions = Apollo.BaseMutationOptions; \ No newline at end of file +export type GetProjectsQueryHookResult = ReturnType; +export type GetProjectsLazyQueryHookResult = ReturnType; +export type GetProjectsQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/packages/client/src/graphql/study/study.graphql b/packages/client/src/graphql/study/study.graphql new file mode 100644 index 00000000..1574ae94 --- /dev/null +++ b/packages/client/src/graphql/study/study.graphql @@ -0,0 +1,14 @@ +query findStudies($project: ID!) { + findStudies(project: $project) { + _id, + name, + description, + instructions, + project, + tagsPerEntry, + tagSchema { + dataSchema, + uiSchema + } + } +} diff --git a/packages/client/src/graphql/study/study.ts b/packages/client/src/graphql/study/study.ts new file mode 100644 index 00000000..1ed2cd74 --- /dev/null +++ b/packages/client/src/graphql/study/study.ts @@ -0,0 +1,59 @@ +/* Generated File DO NOT EDIT. */ +/* tslint:disable */ +import * as Types from '../graphql'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type FindStudiesQueryVariables = Types.Exact<{ + project: Types.Scalars['ID']['input']; +}>; + + +export type FindStudiesQuery = { __typename?: 'Query', findStudies: Array<{ __typename?: 'Study', _id: string, name: string, description: string, instructions: string, project: string, tagsPerEntry: number, tagSchema: { __typename?: 'TagSchema', dataSchema: any, uiSchema: any } }> }; + + +export const FindStudiesDocument = gql` + query findStudies($project: ID!) { + findStudies(project: $project) { + _id + name + description + instructions + project + tagsPerEntry + tagSchema { + dataSchema + uiSchema + } + } +} + `; + +/** + * __useFindStudiesQuery__ + * + * To run a query within a React component, call `useFindStudiesQuery` and pass it any options that fit your needs. + * When your component renders, `useFindStudiesQuery` 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 } = useFindStudiesQuery({ + * variables: { + * project: // value for 'project' + * }, + * }); + */ +export function useFindStudiesQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FindStudiesDocument, options); + } +export function useFindStudiesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FindStudiesDocument, options); + } +export type FindStudiesQueryHookResult = ReturnType; +export type FindStudiesLazyQueryHookResult = ReturnType; +export type FindStudiesQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/packages/client/src/pages/LoginPage.tsx b/packages/client/src/pages/LoginPage.tsx index 9333197a..5068676b 100644 --- a/packages/client/src/pages/LoginPage.tsx +++ b/packages/client/src/pages/LoginPage.tsx @@ -10,13 +10,11 @@ export const LoginPage: FC = () => { const projectId = import.meta.env.VITE_AUTH_PROJECT_ID; const redirectUrl = encodeURIComponent(window.location.origin + '/callback'); const authUrl = `${authUrlBase}/?projectId=${projectId}&redirectUrl=${redirectUrl}`; - console.log(authUrl); const { authenticated } = useAuth(); const navigate = useNavigate(); useEffect(() => { - console.log(authenticated); if (authenticated) { navigate('/'); } else { diff --git a/packages/server/schema.gql b/packages/server/schema.gql index 3e92a494..92f4564d 100644 --- a/packages/server/schema.gql +++ b/packages/server/schema.gql @@ -87,7 +87,7 @@ type Query { projectExists(name: String!): Boolean! getProjects: [Project!]! studyExists(name: String!, project: ID!): Boolean! - findStudies: [Study!]! + findStudies(project: ID!): [Study!]! entryForDataset(dataset: ID!): [Entry!]! } diff --git a/packages/server/src/study/study.model.ts b/packages/server/src/study/study.model.ts index 9a9cbe70..576589d1 100644 --- a/packages/server/src/study/study.model.ts +++ b/packages/server/src/study/study.model.ts @@ -8,11 +8,11 @@ import { Schema as JSONSchema } from 'jsonschema'; @Schema() @ObjectType() export class TagSchema { - @Prop({ type: mongoose.Schema.Types.Mixed }) + @Prop({ type: mongoose.Schema.Types.Mixed, required: true }) @Field(() => JSON) dataSchema: JSONSchema; - @Prop({ type: mongoose.Schema.Types.Mixed }) + @Prop({ type: mongoose.Schema.Types.Mixed, required: true }) @Field(() => JSON) uiSchema: any; } @@ -40,7 +40,7 @@ export class Study { @Field() instructions: string; - @Prop({ type: TagSchemaSchema }) + @Prop({ type: TagSchemaSchema, required: true }) @Field(() => TagSchema) tagSchema: TagSchema; diff --git a/packages/server/src/study/study.resolver.ts b/packages/server/src/study/study.resolver.ts index 148193d1..2d95a3a1 100644 --- a/packages/server/src/study/study.resolver.ts +++ b/packages/server/src/study/study.resolver.ts @@ -24,8 +24,8 @@ export class StudyResolver { // TODO: Replace with user specific study query @Query(() => [Study]) - async findStudies(): Promise { - return this.studyService.findAll(); + async findStudies(@Args('project', { type: () => ID }, ProjectPipe) project: Project): Promise { + return this.studyService.findAll(project); } @Mutation(() => Boolean) diff --git a/packages/server/src/study/study.service.ts b/packages/server/src/study/study.service.ts index 020daee2..274cd54b 100644 --- a/packages/server/src/study/study.service.ts +++ b/packages/server/src/study/study.service.ts @@ -4,6 +4,7 @@ import { InjectModel } from '@nestjs/mongoose'; import { Study } from './study.model'; import { StudyCreate } from './dtos/create.dto'; import { Validator } from 'jsonschema'; +import { Project } from 'src/project/project.model'; @Injectable() export class StudyService { @@ -13,8 +14,8 @@ export class StudyService { return this.studyModel.create(study); } - async findAll(): Promise { - return this.studyModel.find({}); + async findAll(project: Project): Promise { + return this.studyModel.find({ project: project._id.toString() }); } async exists(studyName: string, project: string): Promise {
- handleClick(subitem)} - > - {subitem.title} - -