Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/client/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@
"userPermissions": {
"studyAdmin": "Study Admin",
"contributor": "Contributor",
"trained": "Trained"
"trained": "Trained",
"trainingView": "View Training Results",
"noTrainingTags": "No Tags to Show"
},
"projectUserPermissions": {
"projectAdmin": "Project Admin",
Expand Down
25 changes: 15 additions & 10 deletions packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { StudyProvider } from './context/Study.context';
import { ConfirmationProvider } from './context/Confirmation.context';
import { DatasetProvider } from './context/Dataset.context';
import { EntryControls } from './pages/studies/EntryControls';
import { PermissionProvider } from './context/Permission.context';
import { TagTrainingView } from './pages/studies/TagTrainingView';

const drawerWidth = 256;
const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })<{
Expand Down Expand Up @@ -90,17 +92,19 @@ const AppInternal: FC = () => {
<ProjectProvider>
<StudyProvider>
<DatasetProvider>
<Box>
<NavBar drawerOpen={drawerOpen} setDrawerOpen={setDrawerOpen} />
</Box>
<Main open={drawerOpen}>
<Box sx={{ display: 'flex' }}>
<SideBar open={drawerOpen} drawerWidth={drawerWidth} />
<Box sx={{ flexGrow: 1, width: '90%' }}>
<MyRoutes />
</Box>
<PermissionProvider>
<Box>
<NavBar drawerOpen={drawerOpen} setDrawerOpen={setDrawerOpen} />
</Box>
</Main>
<Main open={drawerOpen}>
<Box sx={{ display: 'flex' }}>
<SideBar open={drawerOpen} drawerWidth={drawerWidth} />
<Box sx={{ flexGrow: 1, width: '90%' }}>
<MyRoutes />
</Box>
</Box>
</Main>
</PermissionProvider>
</DatasetProvider>
</StudyProvider>
</ProjectProvider>
Expand All @@ -126,6 +130,7 @@ const MyRoutes: FC = () => {
<Route path={'/study/permissions'} element={<StudyUserPermissions />} />
<Route path={'/study/entries'} element={<EntryControls />} />
<Route path={'/study/tags'} element={<TagView />} />
<Route path={'/study/training'} element={<TagTrainingView />} />
<Route path={'/successpage'} element={<SuccessPage />} />
<Route path={'/dataset/controls'} element={<DatasetControls />} />
<Route path={'/dataset/projectaccess'} element={<ProjectAccess />} />
Expand Down
17 changes: 3 additions & 14 deletions packages/client/src/components/SideBar.component.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { FC, ReactNode, useEffect, useState } from 'react';
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/Auth.context';
import { useNavigate } from 'react-router-dom';
import { Environment } from './Environment.component';
import { Permission } from '../graphql/graphql';
import { useGetRolesQuery } from '../graphql/permission/permission';
import { useProject } from '../context/Project.context';
import { useStudy } from '../context/Study.context';
import { useTranslation } from 'react-i18next';
import { LanguageSelector } from './LanguageSelector';
import { usePermission } from '../context/Permission.context';

interface SideBarProps {
open: boolean;
Expand All @@ -19,17 +17,8 @@ interface SideBarProps {
export const SideBar: FC<SideBarProps> = ({ open, drawerWidth }) => {
const { logout } = useAuth();
const navigate = useNavigate();
const [permission, setPermission] = useState<Permission | null>(null);
const { project } = useProject();
const { study } = useStudy();
const rolesQueryResults = useGetRolesQuery({ variables: { project: project?._id, study: study?._id } });
const { t } = useTranslation();

useEffect(() => {
if (rolesQueryResults.data) {
setPermission(rolesQueryResults.data.getRoles);
}
}, [rolesQueryResults.data]);
const { permission } = usePermission();

const navItems: NavItemProps[] = [
{
Expand Down
2 changes: 0 additions & 2 deletions packages/client/src/components/TagTraining.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ export const TagTrainingComponent: React.FC<TagTrainingComponentProps> = (props)
props.setTrainingSet(entries);
}, [trainingSet]);

// TODO: In the future, the datasets retrieved should only be datasets
// accessible by the current project
useEffect(() => {
if (getDatasetsResults.data) {
setDatasets(getDatasetsResults.data.getDatasetsByProject);
Expand Down
17 changes: 4 additions & 13 deletions packages/client/src/components/tag/view/TagGridView.component.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useTranslation } from 'react-i18next';
import { GetGridColDefs, TagViewTest } from '../../../types/TagColumnView';
import { Study, Entry } from '../../../graphql/graphql';
import { Entry, Study } from '../../../graphql/graphql';
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
import { DataGrid } from '@mui/x-data-grid';
import { GetTagsQuery, useGetTagsQuery } from '../../../graphql/tag/tag';
import { useEffect, useState } from 'react';
import { GetTagsQuery } from '../../../graphql/tag/tag';
import { freeTextTest, getTextCols } from './FreeTextGridView.component';
import { EntryView } from '../../EntryView.component';
import { Checkbox } from '@mui/material';
Expand All @@ -16,11 +15,11 @@ import { getVideoCols, videoViewTest } from './VideoGridView.component';

export interface TagGridViewProps {
study: Study;
tags: GetTagsQuery['getTags'];
}

export const TagGridView: React.FC<TagGridViewProps> = ({ study }) => {
export const TagGridView: React.FC<TagGridViewProps> = ({ tags, study }) => {
const { t } = useTranslation();
const [tags, setTags] = useState<GetTagsQuery['getTags']>([]);

const tagColumnViews: { tester: TagViewTest; getGridColDefs: GetGridColDefs }[] = [
{ tester: freeTextTest, getGridColDefs: getTextCols },
Expand All @@ -31,14 +30,6 @@ export const TagGridView: React.FC<TagGridViewProps> = ({ study }) => {
{ tester: videoViewTest, getGridColDefs: getVideoCols }
];

const getTagsResults = useGetTagsQuery({ variables: { study: study._id } });

useEffect(() => {
if (getTagsResults.data) {
setTags(getTagsResults.data.getTags);
}
}, [getTagsResults.data]);

const entryColumns: GridColDef[] = [
{
field: 'entryView',
Expand Down
33 changes: 33 additions & 0 deletions packages/client/src/context/Permission.context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ReactNode, createContext, useContext, useEffect, useState } from 'react';
import { Permission } from '../graphql/graphql';
import { useProject } from './Project.context';
import { useStudy } from './Study.context';
import { useGetRolesQuery } from '../graphql/permission/permission';

interface PermissionContextProps {
permission: Permission | null;
}

const PermissionContext = createContext<PermissionContextProps>({} as PermissionContextProps);

export interface PermissionProviderProps {
children: ReactNode;
}

export const PermissionProvider: React.FC<PermissionProviderProps> = ({ children }) => {
const [permission, setPermission] = useState<Permission | null>(null);
const { project } = useProject();
const { study } = useStudy();

const rolesQueryResult = useGetRolesQuery({ variables: { project: project?._id, study: study?._id } });

useEffect(() => {
if (rolesQueryResult.data) {
setPermission(rolesQueryResult.data.getRoles);
}
}, [rolesQueryResult]);

return <PermissionContext.Provider value={{ permission }}>{children}</PermissionContext.Provider>;
};

export const usePermission = () => useContext(PermissionContext);
10 changes: 8 additions & 2 deletions packages/client/src/context/Tag.context.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ReactNode, FC, createContext, useContext, useEffect, useState } from 'react';
import { useStudy } from './Study.context';
import { AssignTagMutation, useAssignTagMutation } from '../graphql/tag/tag';
import { usePermission } from './Permission.context';

export interface TagContextProps {
tag: AssignTagMutation['assignTag'] | null;
training: boolean;
requestTag: () => void;
}

Expand All @@ -17,10 +19,14 @@ export const TagProvider: FC<TagProviderProps> = ({ children }) => {
const { study } = useStudy();
const [tag, setTag] = useState<AssignTagMutation['assignTag'] | null>(null);
const [assignTag, assignTagResult] = useAssignTagMutation();
const { permission } = usePermission();
const [training, setTraining] = useState<boolean>(false);

useEffect(() => {
requestTag();
}, [study]);

setTraining(permission ? !permission.trainedContributor : false);
}, [permission]);

useEffect(() => {
setTag(assignTagResult.data?.assignTag);
Expand All @@ -35,7 +41,7 @@ export const TagProvider: FC<TagProviderProps> = ({ children }) => {
assignTag({ variables: { study: study._id }, fetchPolicy: 'network-only' });
};

return <TagContext.Provider value={{ tag, requestTag }}>{children}</TagContext.Provider>;
return <TagContext.Provider value={{ tag, requestTag, training }}>{children}</TagContext.Provider>;
};

export const useTag = () => useContext(TagContext);
7 changes: 7 additions & 0 deletions packages/client/src/graphql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ export type Query = {
getRoles: Permission;
getStudyPermissions: Array<StudyPermissionModel>;
getTags: Array<Tag>;
getTrainingTags: Array<Tag>;
isEntryEnabled: Scalars['Boolean']['output'];
lexFindAll: Array<Lexicon>;
lexiconByKey: LexiconEntry;
Expand Down Expand Up @@ -438,6 +439,12 @@ export type QueryGetTagsArgs = {
};


export type QueryGetTrainingTagsArgs = {
study: Scalars['ID']['input'];
user: Scalars['String']['input'];
};


export type QueryIsEntryEnabledArgs = {
entry: Scalars['ID']['input'];
study: Scalars['ID']['input'];
Expand Down
19 changes: 19 additions & 0 deletions packages/client/src/graphql/tag/tag.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,22 @@ query getTags($study: ID!) {
}
}

query getTrainingTags($study: ID!, $user: String!) {
getTrainingTags(study: $study, user: $user) {
_id
entry {
_id
organization
entryID
contentType
dataset
creator
dateCreated
meta
signedUrl
signedUrlExpiration
}
data
complete
}
}
60 changes: 59 additions & 1 deletion packages/client/src/graphql/tag/tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ export type GetTagsQueryVariables = Types.Exact<{

export type GetTagsQuery = { __typename?: 'Query', getTags: Array<{ __typename?: 'Tag', _id: string, data?: any | null, complete: boolean, entry: { __typename?: 'Entry', _id: string, organization: string, entryID: string, contentType: string, dataset: string, creator: string, dateCreated: any, meta?: any | null, signedUrl: string, signedUrlExpiration: number } }> };

export type GetTrainingTagsQueryVariables = Types.Exact<{
study: Types.Scalars['ID']['input'];
user: Types.Scalars['String']['input'];
}>;


export type GetTrainingTagsQuery = { __typename?: 'Query', getTrainingTags: Array<{ __typename?: 'Tag', _id: string, data?: any | null, complete: boolean, entry: { __typename?: 'Entry', _id: string, organization: string, entryID: string, contentType: string, dataset: string, creator: string, dateCreated: any, meta?: any | null, signedUrl: string, signedUrlExpiration: number } }> };


export const CreateTagsDocument = gql`
mutation createTags($study: ID!, $entries: [ID!]!) {
Expand Down Expand Up @@ -364,4 +372,54 @@ export function useGetTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<Ge
}
export type GetTagsQueryHookResult = ReturnType<typeof useGetTagsQuery>;
export type GetTagsLazyQueryHookResult = ReturnType<typeof useGetTagsLazyQuery>;
export type GetTagsQueryResult = Apollo.QueryResult<GetTagsQuery, GetTagsQueryVariables>;
export type GetTagsQueryResult = Apollo.QueryResult<GetTagsQuery, GetTagsQueryVariables>;
export const GetTrainingTagsDocument = gql`
query getTrainingTags($study: ID!, $user: String!) {
getTrainingTags(study: $study, user: $user) {
_id
entry {
_id
organization
entryID
contentType
dataset
creator
dateCreated
meta
signedUrl
signedUrlExpiration
}
data
complete
}
}
`;

/**
* __useGetTrainingTagsQuery__
*
* To run a query within a React component, call `useGetTrainingTagsQuery` and pass it any options that fit your needs.
* When your component renders, `useGetTrainingTagsQuery` 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 } = useGetTrainingTagsQuery({
* variables: {
* study: // value for 'study'
* user: // value for 'user'
* },
* });
*/
export function useGetTrainingTagsQuery(baseOptions: Apollo.QueryHookOptions<GetTrainingTagsQuery, GetTrainingTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GetTrainingTagsQuery, GetTrainingTagsQueryVariables>(GetTrainingTagsDocument, options);
}
export function useGetTrainingTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetTrainingTagsQuery, GetTrainingTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<GetTrainingTagsQuery, GetTrainingTagsQueryVariables>(GetTrainingTagsDocument, options);
}
export type GetTrainingTagsQueryHookResult = ReturnType<typeof useGetTrainingTagsQuery>;
export type GetTrainingTagsLazyQueryHookResult = ReturnType<typeof useGetTrainingTagsLazyQuery>;
export type GetTrainingTagsQueryResult = Apollo.QueryResult<GetTrainingTagsQuery, GetTrainingTagsQueryVariables>;
4 changes: 2 additions & 2 deletions packages/client/src/pages/contribute/ContributeLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
const ContributeLandingInternal: React.FC = () => {
const navigate = useNavigate();
const { study } = useStudy();
const { tag } = useTag();
const { tag, training } = useTag();
const { t } = useTranslation();

const enterTagging = () => {
Expand All @@ -26,7 +26,7 @@ const ContributeLandingInternal: React.FC = () => {
<Box sx={{ justifyContent: 'center' }}>
<Stack spacing={3} direction="column" sx={{ maxWidth: 300 }}>
<Typography variant="h3">
{false ? t('components.contribute.studyTraining') : t('components.contribute.studyTagging')}
{training ? t('components.contribute.studyTraining') : t('components.contribute.studyTagging')}
</Typography>
<Typography variant="body2">
{t('common.study')}: {study.name}
Expand Down
33 changes: 33 additions & 0 deletions packages/client/src/pages/studies/TagTrainingView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useLocation } from 'react-router-dom';
import { User, Study } from '../../graphql/graphql';
import { GetTagsQuery, useGetTrainingTagsQuery } from '../../graphql/tag/tag';
import { useEffect, useState } from 'react';
import { TagGridView } from '../../components/tag/view/TagGridView.component';
import { Typography } from '@mui/material';
import { useTranslation } from 'react-i18next';

export const TagTrainingView: React.FC = () => {
const state = useLocation().state;
const user: User = state.user;
const study: Study = state.study;
const [tags, setTags] = useState<GetTagsQuery['getTags']>([]);
const { t } = useTranslation();

const trainingTags = useGetTrainingTagsQuery({ variables: { study: study._id, user: user.uid } });

useEffect(() => {
if (trainingTags.data) {
setTags(trainingTags.data.getTrainingTags);
}
}, [trainingTags]);

return (
<>
{!tags || tags.length === 0 ? (
<Typography variant="h3">{t('components.userPermissions.noTrainingTags')}</Typography>
) : (
<TagGridView tags={tags} study={study} />
)}
</>
);
};
Loading