diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c6ef99..dc47d14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
### Updated
=======
+- Updated app/template/[templateId]/section/create page to use Remirror text editors, and checkboxes with info popovers [#187]
+- Updated DMPEditor to use a skeleton while the text editors are loading, since it can be slow [#187]
- Updated app/template/[templateId]/section/new page to hook it up to backend data, handle errors, and add translations [#189]
- Updated app/template/[templateId] page to hook it up to the backend and handle errors and translations[#206]
- Updated app/[locale]/template page to hook it up to backend and handle errors and translations[#82]
diff --git a/app/[locale]/styleguide/page.tsx b/app/[locale]/styleguide/page.tsx
index 3bf5ca3..ea50aca 100644
--- a/app/[locale]/styleguide/page.tsx
+++ b/app/[locale]/styleguide/page.tsx
@@ -687,6 +687,7 @@ function Page() {
Layout Container
The standard {`
wraps content containers to provide
- some common container within the layout container.
@@ -1207,7 +1208,7 @@ function Page() {
TODO: Write about this layout here
- setDrawerOpen(false) }>
+ setDrawerOpen(false)}>
This is the Drawer Content
@@ -1593,7 +1594,7 @@ function Page() {
diff --git a/app/[locale]/template/[templateId]/section/create/__tests__/page.spec.tsx b/app/[locale]/template/[templateId]/section/create/__tests__/page.spec.tsx
new file mode 100644
index 0000000..0e78673
--- /dev/null
+++ b/app/[locale]/template/[templateId]/section/create/__tests__/page.spec.tsx
@@ -0,0 +1,194 @@
+import React from "react";
+import { render, screen, act, fireEvent } from '@/utils/test-utils';
+import {
+ useAddSectionMutation,
+ useTagsQuery,
+} from '@/generated/graphql';
+
+import { axe, toHaveNoViolations } from 'jest-axe';
+import { useParams } from 'next/navigation';
+import { useTranslations as OriginalUseTranslations } from 'next-intl';
+import CreateSectionPage from '../page';
+expect.extend(toHaveNoViolations);
+
+// Mock the useTemplateQuery hook
+jest.mock("@/generated/graphql", () => ({
+ useAddSectionMutation: jest.fn(),
+ useTagsQuery: jest.fn()
+}));
+
+jest.mock('next/navigation', () => ({
+ useParams: jest.fn(),
+}))
+
+// Create a mock for scrollIntoView and focus
+const mockScrollIntoView = jest.fn();
+
+type UseTranslationsType = ReturnType;
+
+// Mock useTranslations from next-intl
+jest.mock('next-intl', () => ({
+ useTranslations: jest.fn(() => {
+ const mockUseTranslations: UseTranslationsType = ((key: string) => key) as UseTranslationsType;
+
+ /*eslint-disable @typescript-eslint/no-explicit-any */
+ mockUseTranslations.rich = (
+ key: string,
+ values?: Record
+ ) => {
+ // Handle rich text formatting
+ if (values?.p) {
+ return values.p(key); // Simulate rendering the `p` tag function
+ }
+ return key;
+ };
+
+ return mockUseTranslations;
+ }),
+}));
+
+
+const mockTagsData = {
+ "tags": [
+ {
+ "id": 1,
+ "description": "The types of data that will be collected along with their formats and estimated volumes.",
+ "name": "Data description"
+ },
+ {
+ "id": 2,
+ "description": "Descriptions naming conventions, metadata standards that will be used along with data dictionaries and glossaries",
+ "name": "Data organization & documentation"
+ },
+ {
+ "id": 3,
+ "description": "Who will have access to the data and how that access will be controlled, how the data will be encrypted and relevant compliance with regulations or standards (e.g. HIPAA, GDPR)",
+ "name": "Security & privacy"
+ },
+ {
+ "id": 4,
+ "description": "Ethical considerations during data collection, use or sharing and how informed consent will be obtained from participants",
+ "name": "Ethical considerations"
+ },
+ {
+ "id": 5,
+ "description": "Training that will be provided to team members on data management practices and support for data issues",
+ "name": "Training & support"
+ },
+ {
+ "id": 6,
+ "description": "Policies and procedures for how the data will be shared with collaborators and/or the public, restrictions to access and the licenses and permissions used",
+ "name": "Data sharing"
+ },
+ {
+ "id": 7,
+ "description": "Where the data will be stored, the backup strategy and frequency and how long it will be retained",
+ "name": "Data storage & backup"
+ },
+ {
+ "id": 8,
+ "description": "Methods used to ensure data quality and integrity and any procedures used for validation",
+ "name": "Data quality & integrity"
+ },
+ {
+ "id": 9,
+ "description": "Desriptions of the project team members and their roles",
+ "name": "Roles & responsibilities"
+ },
+ {
+ "id": 10,
+ "description": "Description of the budget available for data collection, use and preservation including software licensing, personnel and storage costs",
+ "name": "Budget"
+ },
+ {
+ "id": 11,
+ "description": "How the data will be collected or generated, primary and secondary sources that will be used and any instruments that will be used",
+ "name": "Data collection"
+ }
+ ]
+};
+
+describe("CreateSectionPage", () => {
+ beforeEach(() => {
+ HTMLElement.prototype.scrollIntoView = mockScrollIntoView;
+ window.scrollTo = jest.fn(); // Called by the wrapping PageHeader
+ const mockTemplateId = 123;
+ const mockUseParams = useParams as jest.Mock;
+
+ // Mock the return value of useParams
+ mockUseParams.mockReturnValue({ templateId: `${mockTemplateId}` });
+ (useTagsQuery as jest.Mock).mockReturnValue({
+ data: mockTagsData,
+ loading: true,
+ error: null,
+ });
+ });
+
+ it("should render correct fields", async () => {
+ (useAddSectionMutation as jest.Mock).mockReturnValue([
+ jest.fn().mockResolvedValueOnce({ data: { key: 'value' } }), // Correct way to mock a resolved promise
+ { loading: false, error: undefined },
+ ]);
+
+ await act(async () => {
+ render(
+
+ );
+ });
+
+ const heading = screen.getByRole('heading', { level: 1 });
+ expect(heading).toHaveTextContent('title');
+ const editQuestionTab = screen.getByRole('tab', { name: 'tabs.editSection' });
+ expect(editQuestionTab).toBeInTheDocument();
+ const editOptionsTab = screen.getByRole('tab', { name: 'tabs.options' });
+ expect(editOptionsTab).toBeInTheDocument();
+ const editLogicTab = screen.getByRole('tab', { name: 'tabs.logic' });
+ expect(editLogicTab).toBeInTheDocument();
+ const sectionNameEditor = screen.getByRole('textbox', { name: /sectionName/i });
+ expect(sectionNameEditor).toBeInTheDocument();
+ const sectionIntroductionEditor = screen.getByRole('textbox', { name: /sectionIntroduction/i });
+ expect(sectionIntroductionEditor).toBeInTheDocument();
+ const sectionRequirementsEditor = screen.getByRole('textbox', { name: /sectionRequirements/i });
+ expect(sectionRequirementsEditor).toBeInTheDocument();
+ const sectionGuidanceEditor = screen.getByRole('textbox', { name: /sectionGuidance/i });
+ expect(sectionGuidanceEditor).toBeInTheDocument();
+ const tagsHeader = screen.getByText('labels.bestPracticeTags');
+ expect(tagsHeader).toBeInTheDocument();
+ const checkboxLabels = screen.getAllByTestId('checkboxLabel');
+ expect(checkboxLabels).toHaveLength(11);
+ });
+
+ it('should display error when no value is entered in section name field', async () => {
+ (useAddSectionMutation as jest.Mock).mockReturnValue([
+ jest.fn().mockResolvedValueOnce({ data: { key: 'value' } }), // Correct way to mock a resolved promise
+ { loading: false, error: undefined },
+ ]);
+
+ await act(async () => {
+ render(
+
+ );
+ });
+
+ const searchButton = screen.getByRole('button', { name: /button.createSection/i });
+ fireEvent.click(searchButton);
+
+ const errorMessage = screen.getByRole('alert');
+ expect(errorMessage).toBeInTheDocument();
+ expect(errorMessage).toHaveTextContent('messages.fieldLengthValidation');
+ })
+
+ it('should pass axe accessibility test', async () => {
+ (useAddSectionMutation as jest.Mock).mockReturnValue([
+ jest.fn().mockResolvedValueOnce({ data: { key: 'value' } }),
+ ]);
+
+ const { container } = render(
+
+ );
+ await act(async () => {
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+ });
+ });
+});
\ No newline at end of file
diff --git a/app/[locale]/template/[templateId]/section/create/page.tsx b/app/[locale]/template/[templateId]/section/create/page.tsx
new file mode 100644
index 0000000..4671cde
--- /dev/null
+++ b/app/[locale]/template/[templateId]/section/create/page.tsx
@@ -0,0 +1,417 @@
+'use client';
+
+import React, { useEffect, useRef, useState } from 'react';
+import { ApolloError } from '@apollo/client';
+import { useParams } from 'next/navigation';
+import { useTranslations } from 'next-intl';
+import {
+ Breadcrumb,
+ Breadcrumbs,
+ Button,
+ Checkbox,
+ CheckboxGroup,
+ Form,
+ Label,
+ Link,
+ Tab,
+ TabList,
+ TabPanel,
+ Tabs,
+ DialogTrigger,
+ OverlayArrow,
+ Popover,
+ Dialog,
+} from "react-aria-components";
+// GraphQL queries and mutations
+import {
+ useAddSectionMutation,
+ useTagsQuery,
+} from '@/generated/graphql';
+
+//Components
+import {
+ LayoutContainer,
+ ContentContainer,
+} from '@/components/Container';
+import { DmpIcon } from "@/components/Icons";
+import PageHeader from "@/components/PageHeader";
+import { DmpEditor } from "@/components/Editor";
+
+import styles from './sectionCreate.module.scss';
+interface FormInterface {
+ sectionName: string;
+ sectionIntroduction: string;
+ sectionRequirements: string;
+ sectionGuidance: string;
+ sectionTags?: TagsInterface[];
+}
+
+interface FormErrorsInterface {
+ sectionName: string;
+}
+
+interface TagsInterface {
+ id?: number | null;
+ name: string;
+ description?: string | null;
+}
+
+const CreateSectionPage: React.FC = () => {
+
+ // Get templateId param
+ const params = useParams();
+ const { templateId } = params; // From route /template/:templateId/section/create
+
+ //For scrolling to error in page
+ const errorRef = useRef(null);
+
+ //For scrolling to top of page
+ const topRef = useRef(null);
+
+ //Set initial Rich Text Editor field values
+ const [sectionNameContent, setSectionNameContent] = useState('');
+ const [sectionIntroductionContent, setSectionIntroductionContent] = useState('');
+ const [sectionRequirementsContent, setSectionRequirementsContent] = useState('');
+ const [sectionGuidanceContent, setSectionGuidanceContent] = useState('');
+
+ //Keep form field values in state
+ const [formData, setFormData] = useState({
+ sectionName: '',
+ sectionIntroduction: '',
+ sectionRequirements: '',
+ sectionGuidance: '',
+ sectionTags: []
+ })
+
+ // Keep track of which checkboxes have been selected
+ const [selectedTags, setSelectedTags] = useState([]);
+
+ // Save errors in state to display on page
+ const [errors, setErrors] = useState([]);
+ const [successMessage, setSuccessMessage] = useState('');
+ const [fieldErrors, setFieldErrors] = useState({
+ sectionName: '',
+ sectionIntroduction: '',
+ sectionRequirements: '',
+ sectionGuidance: '',
+ });
+
+ // localization keys
+ const Global = useTranslations('Global');
+ const CreateSectionPage = useTranslations('CreateSectionPage');
+
+ //Store selection of tags in state
+ const [tags, setTags] = useState([]);
+
+ // Initialize user addSection mutation
+ const [addSectionMutation] = useAddSectionMutation();
+
+ // Query for all tags
+ const { data: tagsData } = useTagsQuery();
+
+ // Client-side validation of fields
+ const validateField = (name: string, value: string | string[] | undefined) => {
+ let error = '';
+ switch (name) {
+ case 'sectionName':
+ if (!value || value.length <= 2) {
+ error = CreateSectionPage('messages.fieldLengthValidation')
+ }
+ break;
+ }
+
+ setFieldErrors(prevErrors => ({
+ ...prevErrors,
+ [name]: error
+ }));
+ if (error.length > 1) {
+ setErrors(prev => [...prev, error]);
+ }
+
+ return error;
+ }
+
+ // Check whether form is valid before submitting
+ const isFormValid = (): boolean => {
+ // Initialize a flag for form validity
+ let isValid = true;
+ let errors: FormInterface = {
+ sectionName: '',
+ sectionIntroduction: '',
+ sectionRequirements: '',
+ sectionGuidance: '',
+ };
+
+ // Iterate over formData to validate each field
+ Object.keys(formData).forEach((key) => {
+ const name = key as keyof FormErrorsInterface;
+ const value = formData[name];
+
+ // Call validateField to update errors for each field
+ const error = validateField(name, value);
+ if (error) {
+ isValid = false;
+ errors[name] = error;
+ }
+ });
+ return isValid;
+ };
+
+ const clearAllFieldErrors = () => {
+ //Remove all field errors
+ setFieldErrors({
+ sectionName: '',
+ sectionIntroduction: '',
+ sectionRequirements: '',
+ sectionGuidance: '',
+ sectionTags: []
+ });
+ }
+
+ const scrollToTop = (ref: React.MutableRefObject) => {
+ if (ref.current) {
+ ref.current.scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ }
+ }
+
+ // Make GraphQL mutation request to create section
+ const createSection = async () => {
+ try {
+ await addSectionMutation({
+ variables: {
+ input: {
+ templateId: Number(templateId),
+ name: sectionNameContent,
+ introduction: sectionIntroductionContent,
+ requirements: sectionRequirementsContent,
+ guidance: sectionGuidanceContent,
+ displayOrder: 1,
+ tags: selectedTags
+ }
+ }
+ })
+ } catch (error) {
+ if (error instanceof ApolloError) {
+ setErrors(prevErrors => [...prevErrors, error.message]);
+ } else {
+ setErrors(prevErrors => [...prevErrors, CreateSectionPage('messages.errorCreatingSection')]);
+ }
+ }
+ };
+
+ // Handle changes to tag checkbox selection
+ const handleCheckboxChange = (tag: TagsInterface) => {
+ setSelectedTags((prevTags) => {
+ // Check if the tag is already selected
+ const isAlreadySelected = prevTags.some((selectedTag) => selectedTag.id === tag.id);
+
+ if (isAlreadySelected) {
+ // If already selected, remove it
+ return prevTags.filter((selectedTag) => selectedTag.id !== tag.id);
+ } else {
+ // If not selected, add it
+ return [...prevTags, tag];
+ }
+ });
+ };
+
+ // Handle form submit
+ const handleFormSubmit = async (event: React.FormEvent) => {
+ event.preventDefault();
+
+ setSuccessMessage('');
+ setFormData({
+ sectionName: sectionNameContent,
+ sectionIntroduction: '',
+ sectionRequirements: '',
+ sectionGuidance: '',
+ sectionTags: selectedTags
+ })
+
+ clearAllFieldErrors();
+
+ if (isFormValid()) {
+ // Create new section
+ await createSection();
+ setErrors([]); // Clear errors on successful submit
+ // For now, scroll to top of page to provide some feedback that form was successfully submitted
+ // TODO: add flash/toast message to signal to user that form was successfully submitted
+ setSuccessMessage(CreateSectionPage('messages.success'))
+ scrollToTop(topRef);
+ }
+ };
+
+ useEffect(() => {
+ if (tagsData?.tags) {
+ // Remove __typename field from the tags selection
+ /*eslint-disable @typescript-eslint/no-unused-vars*/
+ const cleanedData = tagsData.tags.map(({ __typename, ...fields }) => fields);
+ setTags(cleanedData);
+ }
+ }, [tagsData])
+
+ // If errors when submitting publish form, scroll them into view
+ useEffect(() => {
+ if (errors.length > 0) {
+ scrollToTop(errorRef);
+ }
+ }, [errors]);
+
+ useEffect(() => {
+ setFormData({
+ ...formData,
+ sectionName: sectionNameContent,
+ sectionIntroduction: sectionIntroductionContent,
+ sectionRequirements: sectionRequirementsContent,
+ sectionGuidance: sectionGuidanceContent
+ });
+ }, [sectionNameContent, sectionIntroductionContent, sectionRequirementsContent, sectionGuidanceContent])
+
+
+ return (
+ <>
+
+ {Global('breadcrumbs.home')}
+ {Global('breadcrumbs.templates')}
+ {Global('breadcrumbs.template')}
+ {Global('breadcrumbs.editSection')}
+
+ }
+ actions={null}
+ className=""
+ />
+
+
+
+
+
+
+ {errors && errors.length > 0 &&
+
+ {errors.map((error, index) => (
+ {error}
+ ))}
+
+ }
+
+ {successMessage && (
+
+ {successMessage}
+
+ )}
+
+
+ {CreateSectionPage('tabs.editSection')}
+ {CreateSectionPage('tabs.options')}
+ {CreateSectionPage('tabs.logic')}
+
+
+
+
+
+ {CreateSectionPage('tabs.options')}
+
+
+ {CreateSectionPage('tabs.logic')}
+
+
+
+
+
+
+ >
+ );
+}
+
+export default CreateSectionPage;
diff --git a/app/[locale]/template/[templateId]/section/create/sectionCreate.module.scss b/app/[locale]/template/[templateId]/section/create/sectionCreate.module.scss
new file mode 100644
index 0000000..4f1236c
--- /dev/null
+++ b/app/[locale]/template/[templateId]/section/create/sectionCreate.module.scss
@@ -0,0 +1,47 @@
+.checkboxGroup {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem; /* Add spacing between checkboxes */
+ margin-bottom: 2rem;
+ font-size: var(--fs-base);
+
+ @media (max-width: 768px) {
+ grid-template-columns: 1fr;
+ }
+
+ .checkboxLabel {
+ font-size: var(--fs-base);
+ }
+ .icon {
+ svg {
+ width: 16px;
+ height:16px;
+ fill: var(--brand-primary);
+ }
+ }
+
+ .popoverBtn {
+ display: inline-block;
+
+ background:none;
+ padding: 0 0 0 0.03rem;
+
+ &:hover {
+ background: none;
+ }
+ }
+
+ label {
+ margin-bottom: 0;
+ }
+}
+
+.checkboxWrapper {
+ display: inline;
+}
+
+.checkboxWrapper > div:first-child {
+ display: inline;
+}
+
+
diff --git a/components/Editor/Editor.scss b/components/Editor/Editor.scss
deleted file mode 100644
index 31a88b6..0000000
--- a/components/Editor/Editor.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-
-.dmp-editor {
- position: relative;
-
- .react-aria-Toolbar {
- padding: 0.5em 0.5em;
- background-color: white;
- border-radius:5px 5px 0 0;
- border: 2px solid var(--gray-100);
- }
-
- .remirror-editor.ProseMirror {
- padding: 0.5em 1.5em 0.5em 1em;
- background-color: white;
- max-height: 400px;
- overflow-y: auto;
- border-radius: 0 0 5px 5px;
- border: 2px solid var(--gray-100);
- }
-}
diff --git a/components/Editor/EditorSkeleton.tsx b/components/Editor/EditorSkeleton.tsx
new file mode 100644
index 0000000..aaf4029
--- /dev/null
+++ b/components/Editor/EditorSkeleton.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import styles from './editor.module.scss';
+
+export const EditorSkeleton: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/components/Editor/editor.module.scss b/components/Editor/editor.module.scss
new file mode 100644
index 0000000..e015264
--- /dev/null
+++ b/components/Editor/editor.module.scss
@@ -0,0 +1,53 @@
+
+.dmpEditor {
+ position: relative;
+ margin-bottom: 2rem;
+
+ .dmpEditorToolbar {
+ padding: 0.5em 0.5em;
+ background-color: white;
+ border-radius:5px 5px 0 0;
+ border: 2px solid var(--gray-100);
+ }
+
+ .Prosemirror,
+ .editorProsemirror {
+ padding: 0.5em 1.5em 0.5em 1em;
+ background-color: white;
+ max-height: 400px;
+ overflow-y: auto;
+ border-radius: 0 0 5px 5px;
+ border: 2px solid var(--gray-100);
+ }
+}
+
+.skeleton {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ background: #f0f0f0;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ padding: 16px;
+ min-height: 122px;
+ margin-bottom: 2rem;
+}
+
+.skeletonToolbar {
+ display: flex;
+ gap: 8px;
+}
+
+.skeletonButton {
+ width: 32px;
+ height: 32px;
+ background: #ddd;
+ border-radius: 4px;
+}
+
+.skeletonEditor {
+ flex-grow: 1;
+ background: #eaeaea;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+}
diff --git a/components/Editor/index.tsx b/components/Editor/index.tsx
index d7cfc0e..4231320 100644
--- a/components/Editor/index.tsx
+++ b/components/Editor/index.tsx
@@ -1,6 +1,7 @@
'use client'
-import React, { useEffect, useState } from 'react';
+import React, { memo, useEffect, useState, useMemo } from 'react';
+import { EditorSkeleton } from './EditorSkeleton';
import {
EditorComponent,
@@ -31,7 +32,7 @@ import {
} from 'remirror/extensions';
import 'remirror/styles/all.css';
-import './Editor.scss';
+import styles from './editor.module.scss';
import { DmpIcon } from '@/components/Icons';
import {
@@ -108,7 +109,7 @@ const EditorToolbar = () => {
const active = useActive();
return (
-
+
{
interface DmpEditorProps {
content: string;
setContent: (newContent: string) => void;
+ id?: string;
+ error?: string;
+ labelId?: string;
}
-export function DmpEditor({ content, setContent }: DmpEditorProps) {
+const MemoizedEditorToolbar = memo(EditorToolbar);
+
+export const DmpEditor = memo(({ content, setContent, error, id, labelId }: DmpEditorProps) => {
const [isMounted, setIsMounted] = useState(false);
+ const extensions = useMemo(() => () => [
+ new BoldExtension({}),
+ new ItalicExtension(),
+ new UnderlineExtension({}),
+ new LinkExtension({ autoLink: true }),
+ new BulletListExtension({}),
+ new OrderedListExtension(),
+ new TableExtension({}),
+ new AnnotationExtension({}),
+ ], []);
+
+
const { manager, state, setState } = useRemirror({
- extensions: () => [
- new BoldExtension({}),
- new ItalicExtension(),
- new UnderlineExtension({}),
- new LinkExtension({ autoLink: true }),
- new BulletListExtension({}),
- new OrderedListExtension(),
- new TableExtension({}),
- new AnnotationExtension({}),
- ],
+ extensions,
content,
@@ -223,27 +232,37 @@ export function DmpEditor({ content, setContent }: DmpEditorProps) {
setIsMounted(true)
}, [])
- const handleChange = (newState: EditorState) => {
- const html = prosemirrorNodeToHtml(newState.doc);
+ const handleChange = ((newState: EditorState) => {
+ //prosemirror was adding an empty when no data was passed, so we need to remove it here
+ const html = prosemirrorNodeToHtml(newState.doc).replaceAll('', '');
setContent(html);
setState(newState);
- }
+ });
+
if (!isMounted) {
- return null; // or a loading indicator
+ // Show the skeleton loader because loading of RTE can be slow and cause shifting on page without it
+ return ;
}
return (
-
+
handleChange(state)}
+ attributes={{
+ 'aria-label': id ?? 'Editor input area',
+ 'aria-labelledby': labelId ?? '',
+ 'class': styles.editorProsemirror,
+ 'id': id ?? ''
+ }}
>
-
+
+ {error}
)
-}
+})
diff --git a/generated/graphql.tsx b/generated/graphql.tsx
index 4dae6dc..43eff78 100644
--- a/generated/graphql.tsx
+++ b/generated/graphql.tsx
@@ -23,6 +23,38 @@ export type Scalars = {
URL: { input: any; output: any; }
};
+export type AddProjectContributorInput = {
+ /** The contributor's affiliation URI */
+ affiliationId?: InputMaybe;
+ /** The contributor's email address */
+ email?: InputMaybe;
+ /** The contributor's first/given name */
+ givenName?: InputMaybe;
+ /** The contributor's ORCID */
+ orcid?: InputMaybe;
+ /** The research project */
+ projectId: Scalars['Int']['input'];
+ /** The roles the contributor has on the research project */
+ roles?: InputMaybe>;
+ /** The contributor's last/sur name */
+ surname?: InputMaybe;
+};
+
+export type AddProjectFunderInput = {
+ /** The funder URI */
+ funder: Scalars['String']['input'];
+ /** The funder's unique id/url for the call for submissions to apply for a grant */
+ funderOpportunityNumber?: InputMaybe;
+ /** The funder's unique id/url for the research project (normally assigned after the grant has been awarded) */
+ funderProjectNumber?: InputMaybe;
+ /** The funder's unique id/url for the award/grant (normally assigned after the grant has been awarded) */
+ grantId?: InputMaybe;
+ /** The project */
+ projectId: Scalars['Int']['input'];
+ /** The status of the funding resquest */
+ status?: InputMaybe;
+};
+
/** Input for adding a new QuestionCondition */
export type AddQuestionConditionInput = {
/** The action to take on a QuestionCondition */
@@ -254,13 +286,64 @@ export enum AffiliationType {
Other = 'OTHER'
}
-export type Contributor = Person & {
- __typename?: 'Contributor';
- contributorId?: Maybe;
- dmproadmap_affiliation?: Maybe;
- mbox?: Maybe;
- name: Scalars['String']['output'];
- role: Array;
+/** An answer to a question on a Data Managament Plan (DMP) */
+export type Answer = {
+ __typename?: 'Answer';
+ /** The answer to the question */
+ answerText?: Maybe;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The DMP that the answer belongs to */
+ plan: Plan;
+ /** The question in the template the answer is for */
+ versionedQuestion: VersionedQuestion;
+ /** The question in the template the answer is for */
+ versionedSection: VersionedSection;
+};
+
+export type AnswerComment = {
+ __typename?: 'AnswerComment';
+ /** The answer the comment is associated with */
+ answer: Answer;
+ /** The comment */
+ commentText: Scalars['String']['output'];
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+};
+
+/** The result of the findCollaborator query */
+export type CollaboratorSearchResult = {
+ __typename?: 'CollaboratorSearchResult';
+ /** The collaborator's affiliation */
+ affiliation?: Maybe;
+ /** The collaborator's first/given name */
+ givenName?: Maybe;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The collaborator's ORCID */
+ orcid?: Maybe;
+ /** The collaborator's last/sur name */
+ surName?: Maybe;
};
export type ContributorRole = {
@@ -302,40 +385,34 @@ export type ContributorRoleMutationResponse = {
success: Scalars['Boolean']['output'];
};
-export type DmpRoadmapAffiliation = {
- __typename?: 'DmpRoadmapAffiliation';
- affiliation_id?: Maybe;
- name: Scalars['String']['output'];
-};
-
-export type Dmsp = {
- __typename?: 'Dmsp';
- contact: PrimaryContact;
- contributor?: Maybe>>;
- created?: Maybe;
- description?: Maybe;
- dmp_id: DmspIdentifier;
- dmproadmap_featured?: Maybe;
- dmproadmap_related_identifiers?: Maybe>>;
- dmproadmap_visibility?: Maybe;
- ethical_issues_description?: Maybe;
- ethical_issues_exist: YesNoUnknown;
- ethical_issues_report?: Maybe;
- language?: Maybe;
- modified?: Maybe;
- title: Scalars['String']['output'];
-};
-
-export type DmspIdentifier = {
- __typename?: 'DmspIdentifier';
- identifier: Scalars['DmspId']['output'];
- type: Scalars['String']['output'];
-};
-
-export type Identifier = {
- __typename?: 'Identifier';
- identifier: Scalars['String']['output'];
- type: Scalars['String']['output'];
+export type EditProjectContributorInput = {
+ /** The contributor's affiliation URI */
+ affiliationId?: InputMaybe;
+ /** The contributor's email address */
+ email?: InputMaybe;
+ /** The contributor's first/given name */
+ givenName?: InputMaybe;
+ /** The contributor's ORCID */
+ orcid?: InputMaybe;
+ /** The project contributor */
+ projectContributorId: Scalars['Int']['input'];
+ /** The roles the contributor has on the research project */
+ roles?: InputMaybe>;
+ /** The contributor's last/sur name */
+ surname?: InputMaybe;
+};
+
+export type EditProjectFunderInput = {
+ /** The funder's unique id/url for the call for submissions to apply for a grant */
+ funderOpportunityNumber?: InputMaybe;
+ /** The funder's unique id/url for the research project (normally assigned after the grant has been awarded) */
+ funderProjectNumber?: InputMaybe;
+ /** The funder's unique id/url for the award/grant (normally assigned after the grant has been awarded) */
+ grantId?: InputMaybe;
+ /** The project funder */
+ projectFunderId: Scalars['Int']['input'];
+ /** The status of the funding resquest */
+ status?: InputMaybe;
};
/** The types of object a User can be invited to Collaborate on */
@@ -364,6 +441,20 @@ export type Mutation = {
addAffiliation?: Maybe;
/** Add a new contributor role (URL and label must be unique!) */
addContributorRole?: Maybe;
+ /** Add a comment to an answer within a round of feedback */
+ addFeedbackComment?: Maybe;
+ /** Create a plan */
+ addPlan?: Maybe;
+ /** Answer a question */
+ addPlanAnswer?: Maybe;
+ /** Add a collaborator to a Plan */
+ addPlanCollaborator?: Maybe;
+ /** Add a Contributor to a Plan */
+ addPlanContributor?: Maybe;
+ /** Add a contributor to a research project */
+ addProjectContributor?: Maybe;
+ /** Add a Funder to a research project */
+ addProjectFunder?: Maybe;
/** Create a new Question */
addQuestion: Question;
/** Create a new QuestionCondition associated with a question */
@@ -380,16 +471,44 @@ export type Mutation = {
addUserEmail?: Maybe;
/** Archive a Template (unpublishes any associated PublishedTemplate */
archiveTemplate?: Maybe;
+ /** Mark the feedback round as complete */
+ completeFeedback?: Maybe;
/** Publish the template or save as a draft */
createTemplateVersion?: Maybe;
/** Deactivate the specified user Account (Admin only) */
deactivateUser?: Maybe;
+ /** Download the plan */
+ downloadPlan?: Maybe;
+ /** Edit an answer */
+ editPlanAnswer?: Maybe;
+ /** Update a contributor on the research project */
+ editProjectContributor?: Maybe;
+ /** Update a Funder on the research project */
+ editProjectFunder?: Maybe;
+ /** Change the plan's status to COMPLETE (cannot be done once the plan is PUBLISHED) */
+ markPlanComplete?: Maybe;
+ /** Change the plan's status to DRAFT (cannot be done once the plan is PUBLISHED) */
+ markPlanDraft?: Maybe;
/** Merge the 2 user accounts (Admin only) */
mergeUsers?: Maybe;
+ /** Publish a plan (changes status to PUBLISHED) */
+ publishPlan?: Maybe;
/** Delete an Affiliation (only applicable to AffiliationProvenance == DMPTOOL) */
removeAffiliation?: Maybe;
/** Delete the contributor role */
removeContributorRole?: Maybe;
+ /** Remove a comment to an answer within a round of feedback */
+ removeFeedbackComment?: Maybe;
+ /** Remove a PlanCollaborator from a Plan */
+ removePlanCollaborator?: Maybe;
+ /** Remove a PlanContributor from a Plan */
+ removePlanContributor?: Maybe;
+ /** Remove a research project contributor */
+ removeProjectContributor?: Maybe;
+ /** Remove a research project Funder */
+ removeProjectFunder?: Maybe;
+ /** Remove a PlanFunder from a Plan */
+ removeProjectFunderFromPlan?: Maybe;
/** Delete a Question */
removeQuestion?: Maybe;
/** Remove a QuestionCondition using a specific QuestionCondition id */
@@ -404,6 +523,10 @@ export type Mutation = {
removeUser?: Maybe;
/** Remove an email address from the current user */
removeUserEmail?: Maybe;
+ /** Request a round of admin feedback */
+ requestFeedback?: Maybe;
+ /** Add a Funder to a Plan */
+ selectProjectFunderForPlan?: Maybe;
/** Designate the email as the current user's primary email address */
setPrimaryUserEmail?: Maybe>>;
/** Set the user's ORCID */
@@ -414,6 +537,10 @@ export type Mutation = {
updateContributorRole?: Maybe;
/** Change the current user's password */
updatePassword?: Maybe;
+ /** Chnage a collaborator's accessLevel on a Plan */
+ updatePlanCollaborator?: Maybe;
+ /** Chnage a Contributor's accessLevel on a Plan */
+ updatePlanContributor?: Maybe;
/** Update a Question */
updateQuestion: Question;
/** Update a QuestionCondition for a specific QuestionCondition id */
@@ -430,6 +557,8 @@ export type Mutation = {
updateUserNotifications?: Maybe;
/** Update the current user's information */
updateUserProfile?: Maybe;
+ /** Upload a plan */
+ uploadPlan?: Maybe;
};
@@ -451,6 +580,50 @@ export type MutationAddContributorRoleArgs = {
};
+export type MutationAddFeedbackCommentArgs = {
+ answerId: Scalars['Int']['input'];
+ commentText: Scalars['String']['input'];
+ planFeedbackId: Scalars['Int']['input'];
+};
+
+
+export type MutationAddPlanArgs = {
+ projectId: Scalars['Int']['input'];
+ versionedTemplateId: Scalars['Int']['input'];
+};
+
+
+export type MutationAddPlanAnswerArgs = {
+ answerText?: InputMaybe;
+ planId: Scalars['Int']['input'];
+ versionedQuestionId: Scalars['Int']['input'];
+ versionedSectionId: Scalars['Int']['input'];
+};
+
+
+export type MutationAddPlanCollaboratorArgs = {
+ email: Scalars['String']['input'];
+ planId: Scalars['Int']['input'];
+};
+
+
+export type MutationAddPlanContributorArgs = {
+ planId: Scalars['Int']['input'];
+ projectContributorId: Scalars['Int']['input'];
+ roles?: InputMaybe>;
+};
+
+
+export type MutationAddProjectContributorArgs = {
+ input: AddProjectContributorInput;
+};
+
+
+export type MutationAddProjectFunderArgs = {
+ input: AddProjectFunderInput;
+};
+
+
export type MutationAddQuestionArgs = {
input: AddQuestionInput;
};
@@ -495,6 +668,12 @@ export type MutationArchiveTemplateArgs = {
};
+export type MutationCompleteFeedbackArgs = {
+ planFeedbackId: Scalars['Int']['input'];
+ summaryText?: InputMaybe;
+};
+
+
export type MutationCreateTemplateVersionArgs = {
comment?: InputMaybe;
templateId: Scalars['Int']['input'];
@@ -508,12 +687,50 @@ export type MutationDeactivateUserArgs = {
};
+export type MutationDownloadPlanArgs = {
+ format: PlanDownloadFormat;
+ planId: Scalars['Int']['input'];
+};
+
+
+export type MutationEditPlanAnswerArgs = {
+ answerId: Scalars['Int']['input'];
+ answerText?: InputMaybe;
+};
+
+
+export type MutationEditProjectContributorArgs = {
+ input: EditProjectContributorInput;
+};
+
+
+export type MutationEditProjectFunderArgs = {
+ input: EditProjectFunderInput;
+};
+
+
+export type MutationMarkPlanCompleteArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
+export type MutationMarkPlanDraftArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
export type MutationMergeUsersArgs = {
userIdToBeMerged: Scalars['Int']['input'];
userIdToKeep: Scalars['Int']['input'];
};
+export type MutationPublishPlanArgs = {
+ planId: Scalars['Int']['input'];
+ visibility?: InputMaybe;
+};
+
+
export type MutationRemoveAffiliationArgs = {
affiliationId: Scalars['Int']['input'];
};
@@ -524,6 +741,37 @@ export type MutationRemoveContributorRoleArgs = {
};
+export type MutationRemoveFeedbackCommentArgs = {
+ PlanFeedbackCommentId: Scalars['Int']['input'];
+};
+
+
+export type MutationRemovePlanCollaboratorArgs = {
+ planCollaboratorId: Scalars['Int']['input'];
+};
+
+
+export type MutationRemovePlanContributorArgs = {
+ planContributorId: Scalars['Int']['input'];
+};
+
+
+export type MutationRemoveProjectContributorArgs = {
+ projectContributorId: Scalars['Int']['input'];
+};
+
+
+export type MutationRemoveProjectFunderArgs = {
+ projectFunderId: Scalars['Int']['input'];
+};
+
+
+export type MutationRemoveProjectFunderFromPlanArgs = {
+ planId: Scalars['Int']['input'];
+ projectFunderId: Scalars['Int']['input'];
+};
+
+
export type MutationRemoveQuestionArgs = {
questionId: Scalars['Int']['input'];
};
@@ -555,6 +803,17 @@ export type MutationRemoveUserEmailArgs = {
};
+export type MutationRequestFeedbackArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
+export type MutationSelectProjectFunderForPlanArgs = {
+ planId: Scalars['Int']['input'];
+ projectFunderId: Scalars['Int']['input'];
+};
+
+
export type MutationSetPrimaryUserEmailArgs = {
email: Scalars['String']['input'];
};
@@ -585,6 +844,18 @@ export type MutationUpdatePasswordArgs = {
};
+export type MutationUpdatePlanCollaboratorArgs = {
+ accessLevel: PlanCollaboratorAccessLevel;
+ planCollaboratorId: Scalars['Int']['input'];
+};
+
+
+export type MutationUpdatePlanContributorArgs = {
+ planContributorId: Scalars['Int']['input'];
+ roles?: InputMaybe>;
+};
+
+
export type MutationUpdateQuestionArgs = {
input: UpdateQuestionInput;
};
@@ -614,6 +885,7 @@ export type MutationUpdateTagArgs = {
export type MutationUpdateTemplateArgs = {
+ bestPractice?: InputMaybe;
name: Scalars['String']['input'];
templateId: Scalars['Int']['input'];
visibility: TemplateVisibility;
@@ -629,32 +901,293 @@ export type MutationUpdateUserProfileArgs = {
input: UpdateUserProfileInput;
};
-export type OrganizationIdentifier = {
- __typename?: 'OrganizationIdentifier';
- identifier: Scalars['Ror']['output'];
- type: Scalars['String']['output'];
+
+export type MutationUploadPlanArgs = {
+ fileContent?: InputMaybe;
+ fileName?: InputMaybe;
+ projectId: Scalars['Int']['input'];
};
-export type Person = {
- dmproadmap_affiliation?: Maybe;
- mbox?: Maybe;
- name: Scalars['String']['output'];
+/** A Data Managament Plan (DMP) */
+export type Plan = {
+ __typename?: 'Plan';
+ /** The plan's answers to the template questions */
+ answers?: Maybe>;
+ /** People who are collaborating on the the DMP content */
+ collaborators?: Maybe>;
+ /** People who are contributing to the research project (not just the DMP) */
+ contributors?: Maybe>;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** The DMP ID/DOI for the plan */
+ dmpId?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** Rounds of administrator feedback provided for the Plan */
+ feedback?: Maybe>;
+ /** The funder who is supporting the work defined by the DMP */
+ funders?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The last person to have changed any part of the DMP (add collaborators, answer questions, etc.) */
+ lastUpdatedBy?: Maybe;
+ /** The last time any part of the DMP was updated (add collaborators, answer questions, etc.) */
+ lastUpdatedOn?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The status of the plan */
+ status?: Maybe;
+ /** The template the plan is based on */
+ versionedTemplate: VersionedTemplate;
+ /** The name/title of the plan (typically copied over from the project) */
+ visibility?: Maybe;
};
-export type PersonIdentifier = {
- __typename?: 'PersonIdentifier';
- identifier: Scalars['Orcid']['output'];
- type: Scalars['String']['output'];
+/** A user that that belongs to a different affiliation that can edit the Plan */
+export type PlanCollaborator = {
+ __typename?: 'PlanCollaborator';
+ /** The user's access level */
+ accessLevel?: Maybe;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** The collaborator's email */
+ email: Scalars['String']['output'];
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The user who invited the collaborator */
+ invitedBy?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The plan the collaborator may edit */
+ plan?: Maybe;
+ /** The collaborator (if they have an account) */
+ user?: Maybe;
};
-export type PrimaryContact = Person & {
- __typename?: 'PrimaryContact';
- contact_id?: Maybe;
- dmproadmap_affiliation?: Maybe;
- mbox?: Maybe;
- name: Scalars['String']['output'];
+export enum PlanCollaboratorAccessLevel {
+ /** The user is able to perform all actions on a Plan (typically restricted to the owner/creator) */
+ Admin = 'ADMIN',
+ /** The user is ONLY able to comment on the Plan's answers */
+ Commenter = 'COMMENTER',
+ /** The user is able to comment and edit the Plan's answers, add/edit/delete contributors and research outputs */
+ Editor = 'EDITOR'
+}
+
+/** A person involved with the research project who will appear in the Plan's citation and landing page */
+export type PlanContributor = {
+ __typename?: 'PlanContributor';
+ /** The contributor's affiliation */
+ ProjectContributor?: Maybe;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The Plan */
+ plan: Plan;
+ /** The roles the contributor has for this specific plan (can differ from the project) */
+ roles?: Maybe>;
+};
+
+export enum PlanDownloadFormat {
+ Csv = 'CSV',
+ Docx = 'DOCX',
+ Html = 'HTML',
+ Json = 'JSON',
+ Pdf = 'PDF',
+ Text = 'TEXT'
+}
+
+/** A round of administrative feedback for a Data Managament Plan (DMP) */
+export type PlanFeedback = {
+ __typename?: 'PlanFeedback';
+ /** An overall summary that can be sent to the user upon completion */
+ adminSummary?: Maybe;
+ /** The timestamp that the feedback was marked as complete */
+ completed?: Maybe;
+ /** The admin who completed the feedback round */
+ completedBy?: Maybe;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The specific contextual commentary */
+ feedbackComments?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The plan the user wants feedback on */
+ plan: Plan;
+ /** The timestamp of when the user requested the feedback */
+ requested: Scalars['String']['output'];
+ /** The user who requested the round of feedback */
+ requestedBy: User;
+};
+
+export type PlanFeedbackComment = {
+ __typename?: 'PlanFeedbackComment';
+ /** The round of plan feedback the comment belongs to */
+ PlanFeedback?: Maybe;
+ /** The answer the comment is related to */
+ answer?: Maybe;
+ /** The comment */
+ comment?: Maybe;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
};
+export enum PlanStatus {
+ /** The Plan is ready for submission or download */
+ Complete = 'COMPLETE',
+ /** The Plan is still being written and reviewed */
+ Draft = 'DRAFT',
+ /** The Plan's DMP ID (DOI) has been registered */
+ Published = 'PUBLISHED'
+}
+
+export enum PlanVisibility {
+ /** Visible only to people at the user's (or editor's) affiliation */
+ Organizational = 'ORGANIZATIONAL',
+ /** Visible only to people who have been invited to collaborate (or provide feedback) */
+ Private = 'PRIVATE',
+ /** Visible to anyone */
+ Public = 'PUBLIC'
+}
+
+export type Project = {
+ __typename?: 'Project';
+ /** The research project abstract */
+ abstract?: Maybe;
+ /** People who are contributing to the research project (not just the DMP) */
+ contributors?: Maybe>;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** The estimated date the research project will end (use YYYY-MM-DD format) */
+ endDate?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The funders who are supporting the research project */
+ funders?: Maybe>;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** Whether or not this is test/mock research project */
+ isTestProject?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The type of research being done */
+ researchDomain?: Maybe;
+ /** The estimated date the research project will begin (use YYYY-MM-DD format) */
+ startDate?: Maybe;
+ /** The name/title of the research project */
+ title: Scalars['String']['output'];
+};
+
+/** A person involved with a research project */
+export type ProjectContributor = {
+ __typename?: 'ProjectContributor';
+ /** The contributor's affiliation */
+ affiliation?: Maybe;
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** The contributor's email address */
+ email?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The contributor's first/given name */
+ givenName?: Maybe;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The contributor's ORCID */
+ orcid?: Maybe;
+ /** The research project */
+ project: Project;
+ /** The roles the contributor has on the research project */
+ roles?: Maybe>;
+ /** The contributor's last/sur name */
+ surname?: Maybe;
+};
+
+/** A funder affiliation that is supporting a research project */
+export type ProjectFunder = {
+ __typename?: 'ProjectFunder';
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe>;
+ /** The funder */
+ funder: Affiliation;
+ /** The funder's unique id/url for the call for submissions to apply for a grant */
+ funderOpportunityNumber?: Maybe;
+ /** The funder's unique id/url for the research project (normally assigned after the grant has been awarded) */
+ funderProjectNumber?: Maybe;
+ /** The funder's unique id/url for the award/grant (normally assigned after the grant has been awarded) */
+ grantId?: Maybe;
+ /** The unique identifer for the Object */
+ id?: Maybe;
+ /** The timestamp when the Object was last modifed */
+ modified?: Maybe;
+ /** The user who last modified the Object */
+ modifiedById?: Maybe;
+ /** The project that is seeking (or has aquired) funding */
+ project: Project;
+ /** The status of the funding resquest */
+ status?: Maybe;
+};
+
+/** The status of the funding */
+export enum ProjectFunderStatus {
+ /** The funder did not award the project */
+ Denied = 'DENIED',
+ /** The funding has been awarded to the project */
+ Granted = 'GRANTED',
+ /** The project will be submitting a grant, or has not yet heard back from the funder */
+ Planned = 'PLANNED'
+}
+
export type Query = {
__typename?: 'Query';
_empty?: Maybe;
@@ -666,18 +1199,42 @@ export type Query = {
affiliationTypes?: Maybe>;
/** Perform a search for Affiliations matching the specified name */
affiliations?: Maybe>>;
+ /** Archive a plan */
+ archivePlan?: Maybe;
/** Get the contributor role by it's id */
contributorRoleById?: Maybe;
/** Get the contributor role by it's URL */
contributorRoleByURL?: Maybe;
/** Get all of the contributor role types */
contributorRoles?: Maybe>>;
- /** Get the DMSP by its DMP ID */
- dmspById?: Maybe;
+ /** Search for a User to add as a collaborator */
+ findCollaborator?: Maybe>>;
/** Get all of the supported Languages */
languages?: Maybe>>;
/** Returns the currently logged in user's information */
me?: Maybe;
+ /** Get a specific plan */
+ plan?: Maybe;
+ /** Get all of the Users that are collaborators for the Plan */
+ planCollaborators?: Maybe>>;
+ /** Get all of the Users that are contributors for the specific Plan */
+ planContributors?: Maybe>>;
+ /** Get all rounds of admin feedback for the plan */
+ planFeedback?: Maybe>>;
+ /** Get all of the comments associated with the round of admin feedback */
+ planFeedbackComments?: Maybe>>;
+ /** Get all of the Users that are Funders for the specific Plan */
+ planFunders?: Maybe>>;
+ /** Get all of the comments associated with the round of admin feedback */
+ planQuestionAnswer?: Maybe;
+ /** Get all rounds of admin feedback for the plan */
+ planSectionAnswers?: Maybe>>;
+ /** Get all plans for the research project */
+ plans?: Maybe>>;
+ /** Get all of the Users that a contributors to the research project */
+ projectContributors?: Maybe>>;
+ /** Get all of the Users that a Funders to the research project */
+ projectFunders?: Maybe>>;
/** Search for VersionedQuestions that belong to Section specified by sectionId */
publishedConditionsForQuestion?: Maybe>>;
/** Search for VersionedQuestions that belong to Section specified by sectionId */
@@ -694,6 +1251,8 @@ export type Query = {
questionTypes?: Maybe>>;
/** Get the Questions that belong to the associated sectionId */
questions?: Maybe>>;
+ /** Get all the QuestionTypes */
+ researchDomains?: Maybe>>;
/** Get the specified section */
section?: Maybe;
/** Get all of the VersionedSection for the specified Section ID */
@@ -734,6 +1293,11 @@ export type QueryAffiliationsArgs = {
};
+export type QueryArchivePlanArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
export type QueryContributorRoleByIdArgs = {
contributorRoleId: Scalars['Int']['input'];
};
@@ -744,8 +1308,65 @@ export type QueryContributorRoleByUrlArgs = {
};
-export type QueryDmspByIdArgs = {
- dmspId: Scalars['DmspId']['input'];
+export type QueryFindCollaboratorArgs = {
+ term?: InputMaybe;
+};
+
+
+export type QueryPlanArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlanCollaboratorsArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlanContributorsArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlanFeedbackArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlanFeedbackCommentsArgs = {
+ planFeedbackId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlanFundersArgs = {
+ planId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlanQuestionAnswerArgs = {
+ answerId: Scalars['Int']['input'];
+ questionId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlanSectionAnswersArgs = {
+ planId: Scalars['Int']['input'];
+ sectionId: Scalars['Int']['input'];
+};
+
+
+export type QueryPlansArgs = {
+ projectId: Scalars['Int']['input'];
+};
+
+
+export type QueryProjectContributorsArgs = {
+ projectId: Scalars['Int']['input'];
+};
+
+
+export type QueryProjectFundersArgs = {
+ projectId: Scalars['Int']['input'];
};
@@ -939,17 +1560,33 @@ export type QuestionType = {
usageDescription: Scalars['String']['output'];
};
-export type RelatedIdentifier = {
- __typename?: 'RelatedIdentifier';
- descriptor: Scalars['String']['output'];
- identifier: Scalars['URL']['output'];
- type: Scalars['String']['output'];
- work_type: Scalars['String']['output'];
+/** An aread of research (e.g. Electrical Engineering, Cellular biology, etc.) */
+export type ResearchDomain = {
+ __typename?: 'ResearchDomain';
+ /** The timestamp when the Object was created */
+ created?: Maybe;
+ /** The user who created the Object */
+ createdById?: Maybe;
+ /** A description of the type of research covered by the domain */
+ description?: Maybe;
+ /** Errors associated with the Object */
+ errors?: Maybe