diff --git a/package-lock.json b/package-lock.json
index cf7872e..82988bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"bootstrap-css": "^4.0.0-alpha.5",
"formik": "^2.4.5",
"json-schema-to-typescript": "^13.1.2",
+ "jsonpath": "^1.1.1",
"lodash": "^4.17.21",
"markdown-it": "^14.1.0",
"moment": "^2.30.1",
@@ -36,6 +37,7 @@
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
+ "@types/jsonpath": "^0.2.4",
"@types/markdown-it": "^14.1.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
@@ -1164,6 +1166,13 @@
"version": "7.0.15",
"license": "MIT"
},
+ "node_modules/@types/jsonpath": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@types/jsonpath/-/jsonpath-0.2.4.tgz",
+ "integrity": "sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/linkify-it": {
"version": "5.0.0",
"dev": true,
@@ -2131,7 +2140,6 @@
},
"node_modules/deep-is": {
"version": "0.1.4",
- "dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
@@ -2298,6 +2306,110 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/escodegen": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+ "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/escodegen/node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/escodegen/node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/eslint": {
"version": "9.11.1",
"dev": true,
@@ -2433,6 +2545,18 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/esprima": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz",
+ "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/esquery": {
"version": "1.6.0",
"dev": true,
@@ -2473,7 +2597,6 @@
},
"node_modules/esutils": {
"version": "2.0.3",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@@ -2554,7 +2677,6 @@
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
- "dev": true,
"license": "MIT"
},
"node_modules/fastq": {
@@ -3188,6 +3310,17 @@
"node": ">=6"
}
},
+ "node_modules/jsonpath": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz",
+ "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==",
+ "license": "MIT",
+ "dependencies": {
+ "esprima": "1.2.2",
+ "static-eval": "2.0.2",
+ "underscore": "1.12.1"
+ }
+ },
"node_modules/jwt-decode": {
"version": "4.0.0",
"license": "MIT",
@@ -4328,6 +4461,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/static-eval": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz",
+ "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==",
+ "license": "MIT",
+ "dependencies": {
+ "escodegen": "^1.8.1"
+ }
+ },
"node_modules/std-env": {
"version": "3.7.0",
"dev": true,
@@ -4637,6 +4779,12 @@
"react": ">=15.0.0"
}
},
+ "node_modules/underscore": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
+ "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==",
+ "license": "MIT"
+ },
"node_modules/undici-types": {
"version": "6.19.8",
"license": "MIT"
@@ -4945,7 +5093,6 @@
},
"node_modules/word-wrap": {
"version": "1.2.5",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
diff --git a/package.json b/package.json
index 51e630c..5004d5d 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"bootstrap-css": "^4.0.0-alpha.5",
"formik": "^2.4.5",
"json-schema-to-typescript": "^13.1.2",
+ "jsonpath": "^1.1.1",
"lodash": "^4.17.21",
"markdown-it": "^14.1.0",
"moment": "^2.30.1",
@@ -40,6 +41,7 @@
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
+ "@types/jsonpath": "^0.2.4",
"@types/markdown-it": "^14.1.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
diff --git a/src/App.css b/src/App.css
index 919ce59..7c22587 100644
--- a/src/App.css
+++ b/src/App.css
@@ -225,6 +225,10 @@ p {
border: 1px solid var(--primary);
}
+.b-grey {
+ border: 1px solid var(--grey);
+}
+
.b-none {
border: none;
}
diff --git a/src/app/Types.ts b/src/app/Types.ts
index baae8c0..a1d4f73 100644
--- a/src/app/Types.ts
+++ b/src/app/Types.ts
@@ -59,6 +59,19 @@ export type Filter = {
}[]
};
+/* Type for a form field */
+export type FormField = {
+ jsonPath: string,
+ title: string,
+ description: string,
+ type: string,
+ options?: string[],
+ mapping?: {
+ [option: string]: string
+ },
+ required?: boolean
+};
+
/* Type for a Dropdown item */
export type DropdownItem = {
label: string,
diff --git a/src/app/types/TaxonomicService.d.ts b/src/app/types/TaxonomicService.d.ts
index 5ca8b9e..4360158 100644
--- a/src/app/types/TaxonomicService.d.ts
+++ b/src/app/types/TaxonomicService.d.ts
@@ -282,7 +282,7 @@ export interface TaxonomicService {
* A unique identifier to identify the author; ORCID identifiers are valid
*/
"schema:identifier": string;
- "schema:affiliation": {
+ "schema:Affiliation"?: {
/**
* The type of the affiliation
*/
@@ -324,7 +324,7 @@ export interface TaxonomicService {
/**
* The organisation the maintainer is affiliated with
*/
- "schema:affiliation": {
+ "schema:Affiliation"?: {
/**
* The type of the affiliation
*/
diff --git a/src/components/general/formElements/InputField.tsx b/src/components/general/formElements/InputField.tsx
new file mode 100644
index 0000000..6debd1c
--- /dev/null
+++ b/src/components/general/formElements/InputField.tsx
@@ -0,0 +1,33 @@
+/* Import Dependencies */
+import { Field } from "formik";
+
+
+/* Props Type */
+type Props = {
+ name: string,
+ title: string
+};
+
+
+/**
+ * Component that renders an input field for a free text insert
+ * @param name The name of the form field
+ * @param title The title that should be displayed along the form field
+ * @returns JSX Component
+ */
+const InputField = (props: Props) => {
+ const { name, title } = props;
+
+ return (
+
+ );
+};
+
+export default InputField;
\ No newline at end of file
diff --git a/src/components/search/components/TopBar.tsx b/src/components/search/components/TopBar.tsx
index 1713ffc..98b4e20 100644
--- a/src/components/search/components/TopBar.tsx
+++ b/src/components/search/components/TopBar.tsx
@@ -2,7 +2,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import { useState, useRef } from 'react';
-import { useSearchParams } from 'react-router-dom';
+import { useSearchParams, useNavigate } from 'react-router-dom';
import { Row, Col } from 'react-bootstrap';
/* Import Hooks */
@@ -25,6 +25,7 @@ import { Button } from 'components/general/CustomComponents';
const TopBar = () => {
/* Hooks */
const [searchParams] = useSearchParams();
+ const navigate = useNavigate();
/* Base variables */
const [filtersToggle, setFiltersToggle] = useState(false);
@@ -76,10 +77,7 @@ const TopBar = () => {
window.open('https://docs.google.com/forms/d/e/1FAIpQLSf6ug3jMZnZaZrP0WYv7GgFvnn06QIdi2GFekEqynwsoTCfUQ/viewform?usp=sf_link',
- '_blank',
- 'noopener'
- )}
+ OnClick={() => navigate('/ts/suggestNewTaxonomicService')}
>
Suggest a new service
diff --git a/src/components/taxonomicService/Routes.tsx b/src/components/taxonomicService/Routes.tsx
index 951db15..db4ef63 100644
--- a/src/components/taxonomicService/Routes.tsx
+++ b/src/components/taxonomicService/Routes.tsx
@@ -3,11 +3,13 @@ import { Route } from "react-router-dom";
/* Import Components */
import TaxonomicService from "./TaxonomicService";
+import TaxonomicServiceForm from "./TaxonomicServiceForm";
/* Routes associated with the Taxonomic Service page */
const routes = [
- } />
+ } />,
+ } />
];
export default routes;
\ No newline at end of file
diff --git a/src/components/taxonomicService/TaxonomicService.tsx b/src/components/taxonomicService/TaxonomicService.tsx
index a6d4307..4b32fac 100644
--- a/src/components/taxonomicService/TaxonomicService.tsx
+++ b/src/components/taxonomicService/TaxonomicService.tsx
@@ -1,6 +1,5 @@
/* Import Dependencies */
import classNames from 'classnames';
-import moment from 'moment';
import { useState } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { Container, Row, Col } from 'react-bootstrap';
@@ -132,9 +131,7 @@ const TaxonomicService = () => {
topicDiscipline: taxonomicService.taxonomicService['ods:topicDiscipline'],
geographicArea: taxonomicService.taxonomicService['schema:geographicArea'],
licence: taxonomicService.taxonomicService['schema:license'],
- published: moment(taxonomicService.taxonomicService['schema:datePublished']).format('MMM DD - YYYY'),
- paymentModel: taxonomicService.taxonomicService['schema:FundingScheme']?.['schema:Funding']?.['schema:identifier'],
- fundingProgram: taxonomicService.taxonomicService['schema:FundingScheme']?.['schema:Funding']?.['schema:description']
+ fundingProgram: taxonomicService.taxonomicService['schema:FundingScheme']?.['schema:Funding']?.['schema:identifier']
}}
/>
diff --git a/src/components/taxonomicService/TaxonomicServiceForm.tsx b/src/components/taxonomicService/TaxonomicServiceForm.tsx
new file mode 100644
index 0000000..8190411
--- /dev/null
+++ b/src/components/taxonomicService/TaxonomicServiceForm.tsx
@@ -0,0 +1,61 @@
+/* Import Dependencies */
+import { Container, Row, Col, Card } from 'react-bootstrap';
+
+/* Import Sources */
+import TaxonomicServiceFormJSON from 'sources/forms/TaxonomicServiceForm.json';
+
+/* Import Components */
+import Header from 'components/general/header/Header';
+import Footer from 'components/general/footer/Footer';
+import FormBuilder from './taxonomicServiceFormComponents/FormBuilder';
+
+
+/**
+ * Component that renders the taxonomic service form
+ * @returns JSX Component
+ */
+const TaxonomicServiceForm = () => {
+ return (
+
+ {/* Render Header */}
+
+
+ {/* Home page Body */}
+
+
+
+
+ {/* Form title and description */}
+
+
+
+ Suggest a new taxonomic e-service
+
+
+ Use this form to suggest new taxonomic e-services or tools that should be listed in the CETAF Marketplace.
+ Please fill in the required fields and add as much additional information as you can.
+ The CETAF secretariat will review submissions and may alter your service description before adding
+ it to the marketplace, or reject it if the suggested resource does not meet CETAF requirements of relevance and quality.
+
+
+
+ {/* Form content */}
+
+
+
+
+
+
+
+
+
+
+ {/* Render Footer */}
+ < Footer />
+
+ );
+};
+
+export default TaxonomicServiceForm;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/BooleanField.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/BooleanField.tsx
new file mode 100644
index 0000000..f5b9a77
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/BooleanField.tsx
@@ -0,0 +1,50 @@
+/* Import Dependencies */
+import classNames from 'classnames';
+import { Field } from "formik";
+import { Row, Col } from 'react-bootstrap';
+
+/* Import Types */
+import { FormField } from "app/Types";
+
+
+/* Props Type */
+type Props = {
+ field: FormField
+};
+
+
+/**
+ * Component that renders an input field for a free text insert
+ * @param field The provided form field
+ * @returns JSX Component
+ */
+const BooleanField = (props: Props) => {
+ const { field } = props;
+
+ /* Class Names */
+ const formFieldClass = classNames({
+ 'b-primary': field.required
+ });
+
+ return (
+
+
+
+
+ {field.title}{field.required ? * : ''}
+
+
+
+
+
+
+
+ );
+};
+
+export default BooleanField;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/DateField.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/DateField.tsx
new file mode 100644
index 0000000..222ec26
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/DateField.tsx
@@ -0,0 +1,43 @@
+/* Import Dependencies */
+import DatePicker from 'react-datepicker';
+
+/* Import Types */
+import { FormField } from "app/Types";
+
+
+/* Props Type */
+type Props = {
+ field: FormField,
+ fieldValue: Date,
+ SetFieldValue: Function
+};
+
+
+/**
+ * Component that renders an input field for a date insert
+ * @param field The provided form field
+ * @param fieldValue The current value of the field in the form state
+ * @param SetFieldValue Function to set the value of a field in the form
+ * @returns JSX Component
+ */
+const DateField = (props: Props) => {
+ const { field, fieldValue, SetFieldValue } = props;
+
+ /* Base variables */
+ const jsonPath = field.jsonPath.replace('$', '');
+
+ return (
+
+
+ {field.title}{field.required ? * : ''}
+
+
SetFieldValue(jsonPath, date)}
+ className="w-100 py-1 px-2 br-corner"
+ wrapperClassName="w-100"
+ />
+
+ );
+};
+
+export default DateField;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/FormBuilder.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/FormBuilder.tsx
new file mode 100644
index 0000000..1241aff
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/FormBuilder.tsx
@@ -0,0 +1,207 @@
+/* Import Dependencies */
+import { Formik, Form } from "formik";
+import jp from 'jsonpath';
+import { Row, Col } from 'react-bootstrap';
+
+/* Import Types */
+import { FormField, Dict } from "app/Types";
+
+/* Import Components */
+import BooleanField from "./BooleanField";
+import DateField from "./DateField";
+import FormBuilderFieldArray from "./FormBuilderFieldArray";
+import MultiSelectField from "./MultiSelectField";
+import SelectField from "./SelectField";
+import StringField from "./StringField";
+import StringArrayField from "./StringArrayField";
+import TextField from "./TextField";
+import { Button } from "components/general/CustomComponents";
+
+
+/* Props Type */
+type Props = {
+ formTemplate: {
+ [formSection: string]: {
+ title: string,
+ type: string,
+ jsonPath?: string,
+ fields: FormField[]
+ }
+ }
+};
+
+
+/**
+ * Component that renders a form builder for rendering forms based on JSON files
+ * @param formTemplate The form template to build the form from
+ * @returns JSX Component
+ */
+const FormBuilder = (props: Props) => {
+ const { formTemplate } = props;
+
+ /* Base variables */
+ const formSections: {
+ [section: string]: {
+ type: string,
+ jsonPath: string,
+ fields: FormField[]
+ }
+ } = {};
+ const initialFormValues: Dict = {};
+
+ /**
+ * Function to flatten a JSON path
+ * @returns flattened JSON path string
+ */
+ const FlattenJSONPath = (jsonPath: string): string => {
+ return jsonPath.replaceAll('[', '_').replaceAll(']', '').replaceAll("'", '');
+ };
+
+ /**
+ * Function to determine the initial form field type
+ * @param fieldType
+ */
+ const DetermineInitialFormValue = (fieldType: string) => {
+ switch (fieldType) {
+ case 'boolean':
+ return false;
+ case 'array':
+ return [];
+ case 'multi-string':
+ return [''];
+ case 'ror':
+ return {
+ "schema:identifier": '',
+ "schema:name": ''
+ };
+ default:
+ return '';
+ };
+ };
+
+ /* Construct initial form values */
+ Object.entries(formTemplate).forEach(([_key, formSection]) => {
+ formSections[formSection.title] = {
+ type: formSection.type,
+ jsonPath: formSection.jsonPath ?? '',
+ fields: []
+ };
+
+ /* If is array, push to initial form values */
+ if (formSection.type === 'array') {
+ jp.value(initialFormValues, formSection.jsonPath ?? '', []);
+ }
+
+ formSection.fields.forEach(field => {
+ let jsonPath: string = '';
+
+ if (formSection.type === 'array') {
+ let pathSuffix: string = FlattenJSONPath(field.jsonPath).split('_').at(-1) as string;
+
+ jsonPath = jsonPath.concat(`${formSection.jsonPath ?? ''}[0]['${pathSuffix}']`);
+
+ /* Add to initial form values array zero index */
+ jp.value(initialFormValues, jsonPath, DetermineInitialFormValue(field.type));
+ } else {
+ /* Add to initial form values */
+ jp.value(initialFormValues, field.jsonPath, DetermineInitialFormValue(field.type));
+ }
+
+ /* Push to form fields */
+ formSections[formSection.title].fields.push(field);
+ });
+ });
+
+ /* Function to construct form field based upon given field */
+ const ConstructFormField = (field: FormField, fieldValues?: any, SetFieldValue?: Function) => {
+ switch (field.type) {
+ case 'boolean': {
+ return ;
+ } case 'date': {
+ let dateValue: Date;
+
+ if (fieldValues) {
+ dateValue = new Date(fieldValues);
+ } else {
+ dateValue = new Date();
+ }
+
+ return SetFieldValue?.(fieldName, value)}
+ />;
+ } case 'multi-string': {
+ return ;
+ } case 'select': {
+ return SetFieldValue?.(fieldName, value)}
+ />;
+ } case 'multi-select': {
+ return SetFieldValue?.(fieldName, value)}
+ />;
+ } case 'text': {
+ return ;
+ } default: {
+ return ;
+ }
+ };
+ };
+
+ return (
+
+
{
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ }}
+ >
+ {({ values, setFieldValue }) => (
+
+ )}
+
+
+ );
+};
+
+export default FormBuilder;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/FormBuilderFieldArray.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/FormBuilderFieldArray.tsx
new file mode 100644
index 0000000..2870d74
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/FormBuilderFieldArray.tsx
@@ -0,0 +1,118 @@
+/* Import Dependencies */
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FieldArray } from "formik";
+import jp from 'jsonpath';
+import { cloneDeep } from "lodash";
+import { Row, Col } from "react-bootstrap";
+
+/* Import Utilities */
+import { MakeReadableString } from "app/Utilities";
+
+/* Import Types */
+import { FormField, Dict } from "app/Types";
+
+/* Import Icons */
+import { faX } from "@fortawesome/free-solid-svg-icons";
+
+/* Import Components */
+import { Button } from "components/general/CustomComponents";
+
+
+/* Props Type */
+type Props = {
+ section: Dict,
+ title: string,
+ initialFormValues: Dict
+ values: Dict,
+ formSections: {
+ [section: string]: {
+ type: string;
+ jsonPath: string;
+ fields: FormField[];
+ }
+ },
+ FlattenJSONPath: Function,
+ ConstructFormField: Function
+};
+
+
+const FormBuilderFieldArray = (props: Props) => {
+ const { section, title, initialFormValues, values, formSections, FlattenJSONPath, ConstructFormField } = props;
+
+ return (
+
+ {({ push, remove }) => (
+
+
+
+ {`${title}${title.at(-1) !== 'a' ? 's' : ''}`}
+
+
+ {
+ push(jp.value(initialFormValues, section.jsonPath)[0]);
+ }}
+ >
+
+ {`Add ${title}`}
+
+
+
+
+
+ {jp.value(values, section.jsonPath).map((_fields: Dict, index: number) => {
+ const key = `${section.jsonPath}-${index}`;
+
+ return (
+
+
+
+
+ {`${title} #${index + 1}`}
+
+
+ {jp.value(values, section.jsonPath).length > 1 &&
+
+ remove(index)}
+ >
+
+
+
+ }
+
+
+
+ {formSections[MakeReadableString(FlattenJSONPath(section.jsonPath)).split(' ').slice(1).join(' ')].fields.map(field => {
+ let localField = cloneDeep(field);
+
+ localField.jsonPath = field.jsonPath.replace('index', String(index));
+
+ return (
+
+
+ {ConstructFormField(localField, jp.value(values, localField.jsonPath))}
+
+
+ );
+ })}
+
+
+
+
+ );
+ })}
+
+ )}
+
+ );
+};
+
+export default FormBuilderFieldArray;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/MultiSelectField.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/MultiSelectField.tsx
new file mode 100644
index 0000000..9f1de84
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/MultiSelectField.tsx
@@ -0,0 +1,63 @@
+/* Import Dependencies */
+import Select from "react-select";
+
+/* Import Types */
+import { FormField } from "app/Types";
+
+
+/* Props Type */
+type Props = {
+ field: FormField,
+ SetFieldValue: Function
+};
+
+
+/**
+ * Component that renders a select field for multi selection
+ * @param field The provided form field
+ * @param SetFieldValue Function to set the value of a field in the form
+ * @returns JSX Component
+ */
+const MultiSelectField = (props: Props) => {
+ const { field, SetFieldValue } = props;
+
+ /* Base variables */
+ const jsonPath = field.jsonPath.replace('$', '');
+ const selectItems: {
+ label: string,
+ value: string
+ }[] = [];
+
+ /* Construct select items */
+ field.options?.forEach(option => {
+ selectItems.push({
+ label: field.mapping ? field.mapping[option] : option,
+ value: option
+ });
+ });
+
+ return (
+
+
+ {field.title}{field.required ? * : ''}
+
+
{
+ /* Create array of all dropdown options values */
+ const valuesArray: string[] = [];
+
+ dropdownOptions.forEach(dropdownOption => {
+ valuesArray.push(dropdownOption.value);
+ });
+
+ SetFieldValue(jsonPath, valuesArray);
+ }}
+ />
+
+ );
+};
+
+export default MultiSelectField;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/SelectField.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/SelectField.tsx
new file mode 100644
index 0000000..ad861a5
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/SelectField.tsx
@@ -0,0 +1,53 @@
+/* Import Dependencies */
+import Select from "react-select";
+
+/* Import Types */
+import { FormField } from "app/Types";
+
+
+/* Props Type */
+type Props = {
+ field: FormField,
+ SetFieldValue: Function
+};
+
+
+/**
+ * Component that renders a select field for single selection
+ * @param field The provided form field
+ * @param SetFieldValue Function to set the value of a field in the form
+ * @returns JSX Component
+ */
+const SelectField = (props: Props) => {
+ const { field, SetFieldValue } = props;
+
+ /* Base variables */
+ const jsonPath = field.jsonPath.replace('$', '');
+ const selectItems: {
+ label: string,
+ value: string
+ }[] = [];
+
+ /* Construct select items */
+ field.options?.forEach(option => {
+ selectItems.push({
+ label: option,
+ value: option
+ });
+ });
+
+ return (
+
+
+ {field.title}{field.required ? * : ''}
+
+
SetFieldValue(jsonPath, dropdownOption?.value)}
+ />
+
+ );
+};
+
+export default SelectField;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/StringArrayField.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/StringArrayField.tsx
new file mode 100644
index 0000000..1374128
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/StringArrayField.tsx
@@ -0,0 +1,96 @@
+/* Import Dependencies */
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FieldArray, Field } from "formik";
+import { Row, Col } from 'react-bootstrap';
+
+/* Import Types */
+import { FormField } from "app/Types";
+
+/* Import Icons */
+import { faX } from "@fortawesome/free-solid-svg-icons";
+
+/* Import Components */
+import { Button } from "components/general/CustomComponents";
+
+
+/* Props Type */
+type Props = {
+ field: FormField,
+ fieldValues: string[]
+};
+
+
+/**
+ * Component that returns a section of an array with string fields in the form
+ * @param field The provided form field
+ * @param fieldValues The current values of the field in the form state
+ * @returns JSX Component
+ */
+const StringArrayField = (props: Props) => {
+ const { field, fieldValues } = props;
+
+ /* Base variables */
+ const jsonPath = field.jsonPath.replace('$', '');
+
+ return (
+
+
+ {({ push, remove }) => (
+
+
+
+
+ {field.title}{field.required ? * : ''}
+
+
+
+ push('')}
+ >
+
+ Add field
+
+
+
+
+ {fieldValues.map((_fieldValue, index) => {
+ const key = `${jsonPath}-${index}`;
+
+ return (
+
+
+
+
+ {fieldValues.length > 1 &&
+
+ remove(index)}
+ >
+
+
+
+ }
+
+ );
+ })}
+
+ )}
+
+
+ );
+};
+
+export default StringArrayField;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/StringField.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/StringField.tsx
new file mode 100644
index 0000000..34c47b7
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/StringField.tsx
@@ -0,0 +1,40 @@
+/* Import Dependencies */
+import classNames from 'classnames';
+import { Field } from "formik";
+
+/* Import Types */
+import { FormField } from "app/Types";
+
+
+/* Props Type */
+type Props = {
+ field: FormField
+};
+
+
+/**
+ * Component that renders an input field for a free text insert
+ * @param field The provided form field
+ * @returns JSX Component
+ */
+const StringField = (props: Props) => {
+ const { field } = props;
+
+ /* Class Names */
+ const formFieldClass = classNames({
+ 'b-primary': field.required
+ });
+
+ return (
+
+
+ {field.title}{field.required ? * : ''}
+
+
+
+ );
+};
+
+export default StringField;
\ No newline at end of file
diff --git a/src/components/taxonomicService/taxonomicServiceFormComponents/TextField.tsx b/src/components/taxonomicService/taxonomicServiceFormComponents/TextField.tsx
new file mode 100644
index 0000000..7d6dead
--- /dev/null
+++ b/src/components/taxonomicService/taxonomicServiceFormComponents/TextField.tsx
@@ -0,0 +1,42 @@
+/* Import Dependencies */
+import classNames from 'classnames';
+import { Field } from "formik";
+
+/* Import Types */
+import { FormField } from "app/Types";
+
+
+/* Props Type */
+type Props = {
+ field: FormField
+};
+
+
+/**
+ * Component that renders an input text field for a free, long text insert
+ * @param field The provided form field
+ * @returns JSX Component
+ */
+const TextField = (props: Props) => {
+ const { field } = props;
+
+ /* Class Names */
+ const formFieldClass = classNames({
+ 'b-primary': field.required
+ });
+
+ return (
+
+
+ {field.title}{field.required ? * : ''}
+
+
+
+ );
+};
+
+export default TextField;
\ No newline at end of file
diff --git a/src/sources/dataModel/taxonomic-service.json b/src/sources/dataModel/taxonomic-service.json
index 98d4e89..ebb38f2 100644
--- a/src/sources/dataModel/taxonomic-service.json
+++ b/src/sources/dataModel/taxonomic-service.json
@@ -518,22 +518,17 @@
"description": "The type of the maintainer",
"const": "schema:Person"
},
- "schema:identifier": [
- {
- "type": "string",
- "description": "A unique identifier to identify the maintainer; GitHub identifiers are valid",
- "examples": [
- "https://api.github.com/users/username"
- ]
+ "schema:identifier": {
+ "type": "string",
+ "description": "A unique identifier to identify the maintainer; GitHub and ORCID identifiers are valid",
+ "items": {
+ "type": "string"
},
- {
- "type": "string",
- "description": "A unique identifier to identify the maintainer; ORCID identifiers are valid",
- "examples": [
- "https://orcid.org/0000-0001-9790-9277"
- ]
- }
- ],
+ "examples": [
+ "https://api.github.com/users/username",
+ "https://orcid.org/0000-0001-9790-9277"
+ ]
+ },
"schema:name": {
"type": "string",
"description": "Full name of the maintainer",
diff --git a/src/sources/forms/TaxonomicServiceForm.json b/src/sources/forms/TaxonomicServiceForm.json
new file mode 100644
index 0000000..5ca0536
--- /dev/null
+++ b/src/sources/forms/TaxonomicServiceForm.json
@@ -0,0 +1,452 @@
+{
+ "taxonomicService": {
+ "title": "Taxonomic Service",
+ "type": "object",
+ "fields": [
+ {
+ "jsonPath": "$['schema:service']['schema:serviceType']",
+ "title": "Service Type",
+ "description": "A type that defines the kind of taxonomic service",
+ "type": "select",
+ "options": [
+ "AI training dataset",
+ "Community group",
+ "CrowdSourcing",
+ "Data tool",
+ "e-Learning service",
+ "Factsheet",
+ "Identification tool",
+ "Knowledge website",
+ "Mobile app",
+ "Service inventory",
+ "Reference collection",
+ "Specimen dataset not in GBIF"
+ ],
+ "required": true
+ },
+ {
+ "jsonPath": "$['schema:service']['schema:name']",
+ "title": "Name",
+ "description": "The preferred name of the service (can be in any language)",
+ "type": "string",
+ "required": true
+ },
+ {
+ "jsonPath": "$['schema:service']['schema:description']",
+ "title": "Description",
+ "description": "A description of the service (english)",
+ "type": "text",
+ "required": true
+ },
+ {
+ "jsonPath": "$['schema:service']['schema:slogan']",
+ "title": "Slogan",
+ "description": "A slogan or motto associated with the Service (english)",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:service']['schema:logo']",
+ "title": "Logo",
+ "description": "An associated logo URL",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:datePublished']",
+ "title": "Date Published",
+ "description": "Date the service was published, following the ISO Date Time Format yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
+ "type": "date"
+ },
+ {
+ "jsonPath": "$['schema:service']['schema:additionalProperty'][0]",
+ "title": "Update Frequency",
+ "description": "Indicator if the service is frequently updated or not, 'frequently updated' if the resource is updated multiple times a year",
+ "type": "select",
+ "options": [
+ "published once",
+ "frequently updated"
+ ]
+ },
+ {
+ "jsonPath": "$['schema:service']['schema:termsOfService']",
+ "title": "Terms of Service",
+ "description": "URL pointing to Terms of Service, Terms of Use or Terms and Conditions, the legal agreements between a service provider and a person who wants to use that service",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:license']",
+ "title": "Service License",
+ "description": "A license document that applies to this content, typically indicated by URL",
+ "type": "select",
+ "options": [
+ "https://spdx.org/licenses/CC-BY-1.0.json",
+ "https://spdx.org/licenses/CC-BY-2.0.json",
+ "https://spdx.org/licenses/CC-BY-2.5.json",
+ "https://spdx.org/licenses/CC-BY-2.5-AU.json",
+ "https://spdx.org/licenses/CC-BY-3.0.json",
+ "https://spdx.org/licenses/CC-BY-3.0-AT.json",
+ "https://spdx.org/licenses/CC-BY-3.0-AU.json",
+ "https://spdx.org/licenses/CC-BY-3.0-DE.json",
+ "https://spdx.org/licenses/CC-BY-3.0-IGO.json",
+ "https://spdx.org/licenses/CC-BY-3.0-NL.json",
+ "https://spdx.org/licenses/CC-BY-3.0-US.json",
+ "https://spdx.org/licenses/CC-BY-4.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-1.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-2.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-2.5.json",
+ "https://spdx.org/licenses/CC-BY-NC-3.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-3.0-DE.json",
+ "https://spdx.org/licenses/CC-BY-NC-4.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-ND-1.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-ND-2.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-ND-2.5.json",
+ "https://spdx.org/licenses/CC-BY-NC-ND-3.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-ND-3.0-DE.json",
+ "https://spdx.org/licenses/CC-BY-NC-ND-3.0-IGO.json",
+ "https://spdx.org/licenses/CC-BY-NC-ND-4.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-1.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-2.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-2.0-DE.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-2.0-FR.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-2.0-UK.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-2.5.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-3.0.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-3.0-DE.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-3.0-IGO.json",
+ "https://spdx.org/licenses/CC-BY-NC-SA-4.0.json",
+ "https://spdx.org/licenses/CC-BY-ND-1.0.json",
+ "https://spdx.org/licenses/CC-BY-ND-2.0.json",
+ "https://spdx.org/licenses/CC-BY-ND-2.5.json",
+ "https://spdx.org/licenses/CC-BY-ND-3.0.json",
+ "https://spdx.org/licenses/CC-BY-ND-3.0-DE.json",
+ "https://spdx.org/licenses/CC-BY-ND-4.0.json",
+ "https://spdx.org/licenses/CC-BY-SA-1.0.json",
+ "https://spdx.org/licenses/CC-BY-SA-2.0.json",
+ "https://spdx.org/licenses/CC-BY-SA-2.0-UK.json",
+ "https://spdx.org/licenses/CC-BY-SA-2.1-JP.json",
+ "https://spdx.org/licenses/CC-BY-SA-2.5.json",
+ "https://spdx.org/licenses/CC-BY-SA-3.0.json",
+ "https://spdx.org/licenses/CC-BY-SA-3.0-AT.json",
+ "https://spdx.org/licenses/CC-BY-SA-3.0-DE.json",
+ "https://spdx.org/licenses/CC-BY-SA-3.0-IGO.json",
+ "https://spdx.org/licenses/CC-BY-SA-4.0.json",
+ "https://spdx.org/licenses/CC-PDDC.json",
+ "https://spdx.org/licenses/CC0-1.0.json",
+ "https://spdx.org/licenses/DL-DE-BY-2.0.json",
+ "https://spdx.org/licenses/DL-DE-ZERO-2.0.json",
+ "https://www.govdata.de/dl-de/by-1-0",
+ "https://www.govdata.de/dl-de/by-nc-1-0",
+ "https://spdx.org/licenses/EUPL-1.0.json",
+ "https://spdx.org/licenses/EPL-2.0.json",
+ "https://spdx.org/licenses/EUPL-1.2.json",
+ "https://www.etalab.gouv.fr/licence-ouverte-open-licence/",
+ "https://spdx.org/licenses/GFDL-1.1-invariants-only.json",
+ "https://spdx.org/licenses/GFDL-1.1-invariants-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.1-no-invariants-only.json",
+ "https://spdx.org/licenses/GFDL-1.1-no-invariants-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.1-only.json",
+ "https://spdx.org/licenses/GFDL-1.1-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.2-invariants-only.json",
+ "https://spdx.org/licenses/GFDL-1.2-invariants-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.2-no-invariants-only.json",
+ "https://spdx.org/licenses/GFDL-1.2-no-invariants-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.2-only.json",
+ "https://spdx.org/licenses/GFDL-1.2-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.3-invariants-only.json",
+ "https://spdx.org/licenses/GFDL-1.3-invariants-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.3-no-invariants-only.json",
+ "https://spdx.org/licenses/GFDL-1.3-no-invariants-or-later.json",
+ "https://spdx.org/licenses/GFDL-1.3-only.json",
+ "https://spdx.org/licenses/GFDL-1.3-or-later.json",
+ "https://data.gov.hr/open-licence-republic-croatia",
+ "https://www.dati.gov.it/iodl/2.0/",
+ "https://spdx.org/licenses/NLOD-1.0.json",
+ "https://spdx.org/licenses/NLOD-2.0.json",
+ "https://spdx.org/licenses/ODC-By-1.0.json",
+ "https://spdx.org/licenses/ODbL-1.0.json",
+ "https://spdx.org/licenses/PDDL-1.0.json",
+ "https://spdx.org/licenses/OGL-Canada-2.0.json",
+ "https://spdx.org/licenses/OGL-UK-1.0.json",
+ "https://spdx.org/licenses/OGL-UK-2.0.json",
+ "https://spdx.org/licenses/OGL-UK-3.0.json",
+ "https://data.gov.ro/pages/licence"
+ ]
+ },
+ {
+ "jsonPath": "$['schema:availableLanguage']",
+ "title": "Available Languages",
+ "description": "A language someone may use with or at the item, service or place. Please use one of the language codes from the IETF BCP 47 standard",
+ "type": "multi-select",
+ "options": [
+ "bg",
+ "hr",
+ "cs",
+ "da",
+ "lb",
+ "nl",
+ "en",
+ "et",
+ "fi",
+ "fr",
+ "de",
+ "el",
+ "hu",
+ "ga",
+ "it",
+ "lv",
+ "lt",
+ "mt",
+ "pl",
+ "pt",
+ "ro",
+ "ru",
+ "sk",
+ "es",
+ "sv",
+ "tk",
+ "uk"
+ ],
+ "mapping": {
+ "bg": "Bulgarian",
+ "hr": "Croatian",
+ "cs": "Czech",
+ "da": "Danish",
+ "lb": "Luxembourgish",
+ "nl": "Dutch",
+ "en": "English",
+ "et": "Estonian",
+ "fi": "Finnish",
+ "fr": "French",
+ "de": "German",
+ "el": "Greek",
+ "hu": "Hungarian",
+ "ga": "Irish",
+ "it": "Italian",
+ "lv": "Latvian",
+ "lt": "Lithuanian",
+ "mt": "Maltese",
+ "pl": "Polish",
+ "pt": "Portugese",
+ "ro": "Romanian",
+ "ru": "Russian",
+ "sk": "Slovak",
+ "es": "Spanish",
+ "sv": "Swedish",
+ "tk": "Turkish",
+ "uk": "Ukranian"
+ },
+ "required": true
+ },
+ {
+ "jsonPath": "$['ods:topicDiscipline']",
+ "title": "Topic Discipline",
+ "description": "The topic discipline relevant to the taxonomic range of the service",
+ "type": "select",
+ "options": [
+ "Anthropology",
+ "Botany",
+ "Microbiology",
+ "Palaeontology",
+ "Zoology",
+ "Ecology",
+ "Other Biodiversity",
+ "Unclassified"
+ ]
+ },
+ {
+ "jsonPath": "$['schema:taxonomicRange']",
+ "title": "Taxonomic Range",
+ "description": "The taxonomic grouping of the organism, e.g. 'No specific range' or 'Agromyzidae, Braconidae'",
+ "type": "multi-string"
+ },
+ {
+ "jsonPath": "$['schema:additionalProperty'][0]",
+ "title": "Organism Group",
+ "description": "Common names for groups of organisms",
+ "type": "multi-string"
+ },
+ {
+ "jsonPath": "$['schema:geographicArea']",
+ "title": "Geographic Area",
+ "description": "The geographic area associated with the service, e.g. World, Palearctic, South-East Europe, Mediterranean",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:url']",
+ "title": "Webpage",
+ "description": "URL of the item that leads to the resource of the service",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:documentation']",
+ "title": "Documentation URL",
+ "description": "URL to further documentation describing the service in more detail",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:about']",
+ "title": "Change Log",
+ "description": "The subject matter of the service including a summary of the Resource features updated from the previous version",
+ "type": "text"
+ },
+ {
+ "jsonPath": "$['schema:additionalProperty'][1]",
+ "title": "Available on App Store",
+ "description": "Application is available on the following app stores, relevant for apps",
+ "type": "multi-string"
+ },
+ {
+ "jsonPath": "$['schema:additionalProperty'][2]",
+ "title": "Payment Model",
+ "description": "URL to webpage with the supported payment models and restrictions that apply to the Resource",
+ "type": "string"
+ }
+ ]
+ },
+ "contactPoint": {
+ "title": "Contact Point",
+ "type": "object",
+ "fields": [
+ {
+ "jsonPath": "$['schema:contactPoint']['schema:email']",
+ "title": "Contact Email",
+ "description": "Email address of the contact point",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:contactPoint']['schema:url']",
+ "title": "Contact Webpage",
+ "description": "URL to a contact webpage or contact form for the resource",
+ "type": "string"
+ }
+ ]
+ },
+ "author": {
+ "title": "Author",
+ "jsonPath": "$['schema:author']",
+ "type": "array",
+ "fields": [
+ {
+ "jsonPath": "$['schema:author']['index']['schema:identifier']",
+ "title": "ORCID",
+ "description": "A unique identifier to identify the author; ORCID identifiers are valid",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:author']['index']['schema:affiliation']",
+ "title": "Affiliated Organisation",
+ "description": "The affiliated organisation",
+ "type": "ror"
+ }
+ ]
+ },
+ "maintainer": {
+ "title": "Maintainer",
+ "jsonPath": "$['schema:maintainer']",
+ "type": "array",
+ "fields": [
+ {
+ "jsonPath": "$['schema:maintainer']['index']['schema:identifier']",
+ "title": "GitHub / ORCID Identifier",
+ "description": "A unique identifier to identify the maintainer; ORCID and GitHub identifiers are valid",
+ "type": "multi-string"
+ },
+ {
+ "jsonPath": "$['schema:maintainer']['index']['schema:name']",
+ "title": "Full Name",
+ "description": "Full name of the maintainer",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:maintainer']['index']['schema:affiliation']",
+ "title": "Affiliated Organisation",
+ "description": "The affiliated organisation",
+ "type": "ror"
+ }
+ ]
+ },
+ "funding": {
+ "title": "Funding",
+ "type": "object",
+ "fields": [
+ {
+ "jsonPath": "$['schema:fundingscheme']['schema:award']",
+ "title": "Award",
+ "description": "An award won by or for this service",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:fundingscheme']['schema:funding']['schema:identifier']",
+ "title": "Grant Identifier",
+ "description": "A unique identifier to identify the funder organisation; ROR identifiers are valid",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:fundingscheme']['schema:funding']['schema:description']",
+ "title": "Grant Description",
+ "description": "A description of the service's grant",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:fundingscheme']['schema:funding']['schema:funder']",
+ "title": "Funding Organisation",
+ "description": "An organization that supports (sponsors) something through some kind of financial contribution",
+ "type": "ror"
+ }
+ ]
+ },
+ "softwareSourceCode": {
+ "title": "Software Source Code",
+ "type": "object",
+ "fields": [
+ {
+ "jsonPath": "$['schema:softwareSourceCode']['schema:codeRepository']",
+ "title": "Code Repository",
+ "description": "Link to the repository where the un-compiled, human readable code and related code is located (SVN, GitHub, CodePlex)",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:softwareSourceCode']['schema:runtimePlatform']",
+ "title": "Runtime Platform",
+ "description": "Runtime platform or script interpreter dependencies (example: Java v1, Python 2.3, .NET Framework 3.0)",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:softwareSourceCode']['schema:creativeWorkStatus']",
+ "title": "Status",
+ "description": "The status of a creative work in terms of its stage in a lifecycle. Example terms include Incomplete, Draft, Published, Obsolete. Some organizations define a set of terms for the stages of their publication lifecycle",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:softwareSourceCode']['schema:programmingLanguage']",
+ "title": "Programming Languages",
+ "description": "The computer programming language",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:softwareSourceCode']['schema:license']",
+ "title": "Software License",
+ "description": "A license document that applies to this content, typically indicated by URL",
+ "type": "string"
+ },
+ {
+ "jsonPath": "$['schema:softwareSourceCode']['schema:additionalProperty'][0]",
+ "title": "Is Open Source",
+ "description": "Boolean indicating is the software source code is available by open source",
+ "type": "boolean"
+ }
+ ]
+ },
+ "associatedMedia": {
+ "title": "Associated Media",
+ "jsonPath": "$['schema:associatedMedia']",
+ "type": "array",
+ "fields": [
+ {
+ "jsonPath": "$['schema:associatedMedia']['index']['schema:contentUrl']",
+ "title": "Content URL",
+ "description": "JPG, PNG or SVG file showing a screenshot or other relevant illustration of the resource. Add only files that are public domain",
+ "type": "string"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/sources/taxonomicServiceForm/TaxonomicServiceFormFields.json b/src/sources/taxonomicServiceForm/TaxonomicServiceFormFields.json
new file mode 100644
index 0000000..8377cc5
--- /dev/null
+++ b/src/sources/taxonomicServiceForm/TaxonomicServiceFormFields.json
@@ -0,0 +1,256 @@
+{
+ "taxonomicService": {
+ "title": "Taxonomic Service",
+ "formFields": [
+ {
+ "name": "",
+ "title": "Service Type",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Name",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Description",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Slogan",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Logo",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Date Published",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Update Frequency",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Terms of Service",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Service License",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Available Languages",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Topic Discipline",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Taxonomic Range",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Organism Group",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Geographic Area",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Webpage",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Documentation URL",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Change Log",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Payment Model",
+ "description": "",
+ "type": ""
+ }
+ ]
+ },
+ "contactPoint": {
+ "title": "Contact Point",
+ "fields": [
+ {
+ "name": "",
+ "title": "Contact Email",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Contact Webpage",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "",
+ "description": "",
+ "type": ""
+ }
+ ]
+ },
+ "author": {
+ "title": "Author",
+ "type": "array",
+ "fields": [
+ {
+ "name": "",
+ "title": "ORCID",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Affiliated Organisation",
+ "description": "",
+ "type": ""
+ }
+ ]
+ },
+ "maintainer": {
+ "title": "Maintainer",
+ "type": "array",
+ "fields": [
+ {
+ "name": "",
+ "title": "GitHub Identifier",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "ORCID",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Full Name",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Affiliated Organisation",
+ "description": "",
+ "type": "ROR"
+ }
+ ]
+ },
+ "funding": {
+ "title": "Funding",
+ "fields": [
+ {
+ "name": "",
+ "title": "Award",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Grant",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Funding Organisation",
+ "description": "",
+ "type": ""
+ }
+ ]
+ },
+ "softwareSourceCode": {
+ "title": "Software Source Code",
+ "fields": [
+ {
+ "name": "",
+ "title": "Code Repository",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Runtime Platform",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Status",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Programming Languages",
+ "description": "",
+ "type": ""
+ },
+ {
+ "name": "",
+ "title": "Software License",
+ "description": "",
+ "type": ""
+ }
+ ]
+ },
+ "associatedMedia": {
+ "title": "Associated Media",
+ "type": "array",
+ "fields": [
+ {
+ "name": "",
+ "title": "Content URL",
+ "description": "",
+ "type": ""
+ }
+ ]
+ }
+}
\ No newline at end of file